From 1c839fe35de246c6a3e8928e57b9456a37524274 Mon Sep 17 00:00:00 2001 From: leosfreitas Date: Thu, 5 Dec 2024 20:21:35 -0300 Subject: [PATCH] Readme e Documentacao --- README.md | 179 +++++++++++++++++++++++++++++++++++++++++- app/src/App.jsx | 7 ++ back/main.py | 56 ++++++++----- back/requirements.txt | Bin 2208 -> 2168 bytes back/utils.py | 76 +++++++++++++++--- requirements.txt | 2 - 6 files changed, 287 insertions(+), 33 deletions(-) delete mode 100644 requirements.txt diff --git a/README.md b/README.md index a23950f..46c1b09 100644 --- a/README.md +++ b/README.md @@ -1 +1,178 @@ -[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/LUsvMCbe) +# 2024-2-APP-GP-VERDE-APP + +## Descrição do Projeto + +Este é um projeto desenvolvido para a Sprint Session do 4º semestre de 2024/2 no Insper, em parceria com a DASA. O projeto consiste em um sistema completo com: + +- **Frontend:** Implementado em React + Vite. +- **Backend:** Implementado em FastAPI. + +**Link para acessar o site do projeto:** [http://3.81.205.150](http://3.81.205.150) + +## Estrutura do Repositório + +- **`app/`**: Contém o código do frontend (React + Vite). +- **`back/`**: Contém o código do backend (FastAPI). + +--- + +## Instalação do AWS CLI e DVC + +### **1. Instalar o Python** +Certifique-se de que o Python está instalado em sua máquina: + +1. Verifique a versão do Python: + ```bash + python3 --version + ``` +2. Caso não esteja instalado, siga as instruções específicas do seu sistema operacional para instalá-lo. + +### **2. Instalar o Node.js** +Certifique-se de que o Node.js está instalado em sua máquina: + +1. Verifique a versão do Node.js: + ```bash + node --version + ``` +2. Caso não esteja instalado, baixe o instalador do [site oficial do Node.js](https://nodejs.org) e siga as instruções. + +### **3. Instalar o AWS CLI** + +#### **Windows** + +1. Baixe o instalador oficial: [AWS CLI para Windows](https://awscli.amazonaws.com/AWSCLIV2.msi) +2. Execute o arquivo `.msi` e siga as instruções do instalador. +3. Verifique a instalação: + ```bash + aws --version + ``` + +#### **macOS** + +1. Baixe o pacote do instalador: + ```bash + curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg" + ``` +2. Instale com o comando: + ```bash + sudo installer -pkg AWSCLIV2.pkg -target / + ``` +3. Verifique a instalação: + ```bash + aws --version + ``` + +#### **Linux** + +1. Baixe o instalador: + ```bash + curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" + ``` +2. Extraia o arquivo: + ```bash + unzip awscliv2.zip + ``` +3. Execute o instalador: + ```bash + sudo ./aws/install + ``` +4. Verifique a instalação: + ```bash + aws --version + ``` + +### **4. Instalar o DVC** + +#### **Via pip (Recomendado)** + +1. Certifique-se de que o Python está instalado: + ```bash + python3 --version + ``` +2. Instale o DVC: + ```bash + pip install dvc[s3] + ``` +3. Verifique a instalação: + ```bash + dvc --version + ``` + +--- + +## Instalação e Execução do Projeto + +### **1. Clonar o Repositório** + +1. Clone o repositório para a sua máquina local: + ```bash + git clone https://github.com/insper-classroom/2024-2-app-gp-verde-app.git + ``` +2. Entre na pasta do projeto: + ```bash + cd 2024-2-app-gp-verde-app + ``` + +### **2. Configuração Inicial do Repositório** + +1. Configure suas credenciais AWS: + ```bash + aws configure + ``` + Insira as seguintes informações: + - AWS Access Key ID + - AWS Secret Access Key + - Default region (us-east-1) + - Output format (deixe vazio ou "json") + +2. Baixe os arquivos de dados gerenciados pelo DVC: + ```bash + dvc pull + ``` +3. Certifique-se de que todos os arquivos foram baixados corretamente antes de rodar o backend. + +### **3. Configuração do Frontend** + +1. Abra um terminal e entre na pasta do frontend: + ```bash + cd app + ``` +2. Instale as dependências: + ```bash + npm install + ``` +3. Inicie o servidor de desenvolvimento: + ```bash + npm run dev + ``` + O servidor será iniciado localmente em [http://localhost:5173](http://localhost:5173). + +### **4. Configuração do Backend** + +1. Abra outro terminal e entre na pasta do backend: + ```bash + cd back + ``` +2. Instale as dependências: + ```bash + pip install -r requirements.txt + ``` +3. Inicie o servidor do FastAPI: + ```bash + uvicorn main:app --reload + ``` + O servidor será iniciado localmente em [http://127.0.0.1:8000](http://127.0.0.1:8000). + +> **Nota:** É necessário utilizar dois terminais separados para rodar o frontend e o backend simultaneamente. + +--- + +## Contribuições + +Este repositório foi desenvolvido por: + +- Giovanny Russo +- Leonardo Freitas +- Luigi Orlandi +- Thiago Penha + diff --git a/app/src/App.jsx b/app/src/App.jsx index 44f47a9..2ffab64 100644 --- a/app/src/App.jsx +++ b/app/src/App.jsx @@ -1,3 +1,10 @@ +/** + * Este código React implementa uma interface para carregar arquivos .txt, + * enviá-los para um servidor backend e exibir os resultados processados, + * incluindo predições e heatmaps. A interface é estilizada com Material-UI + * e inclui animações com Framer Motion. + */ + import React, { useState } from "react"; import { Box, diff --git a/back/main.py b/back/main.py index 5a2092a..cf740db 100644 --- a/back/main.py +++ b/back/main.py @@ -5,78 +5,91 @@ from utils import * import numpy as np from plot import * -import base64 +import base64 import pickle import os import shutil +# Inicializa a aplicação FastAPI app = FastAPI() +# Configuração de CORS para permitir requisições de qualquer origem origins = ["*"] - app.add_middleware( CORSMiddleware, - allow_origins=origins, + allow_origins=origins, # Permitir todas as origens allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], + allow_methods=["*"], # Permitir todos os métodos HTTP + allow_headers=["*"], # Permitir todos os cabeçalhos ) +# Carregamento do modelo pré-treinado de TensorFlow model = tf.keras.models.load_model("model/model_artigo_v4.keras") +# Carregamento das informações de comprimento dos braços cromossômicos with open("./data/arm_lengths/arm_lengths.pkl", "rb") as f: chr_arms = pickle.load(f) +# Definição de diretórios para uploads e logs TEMP_DIR = "./uploads/coverage" LOG_DIR = "./logs" -os.makedirs(TEMP_DIR, exist_ok=True) -os.makedirs(LOG_DIR, exist_ok=True) +os.makedirs(TEMP_DIR, exist_ok=True) # Garante que o diretório temporário existe +os.makedirs(LOG_DIR, exist_ok=True) # Garante que o diretório de logs existe +# Endpoint raiz para verificação da API @app.get("/") async def root(): return {"message": "Hello World"} +# Endpoint para processar múltiplos arquivos enviados pelo usuário @app.post("/process-multiple-files") async def process_multiple_files(files: list[UploadFile] = File(...)): + """ + Processa múltiplos arquivos de cobertura enviados pelo usuário. + Para cada arquivo: + 1. Salva temporariamente. + 2. Pré-processa os dados e realiza predição. + 3. Gera um heatmap e salva nos logs. + 4. Retorna resultados, incluindo as predições e o heatmap em Base64. + """ results = [] threshold = 0.6518086791038513 # Limiar para classificação + try: for file in files: - # Salvar arquivo temporariamente + # Salvar o arquivo temporariamente cov_file = os.path.join(TEMP_DIR, file.filename) with open(cov_file, "wb") as f: - f.write(await file.read()) + f.write(await file.read()) # Escreve o conteúdo do arquivo try: - # Criar pasta para logs deste arquivo + # Criar subdiretório para logs deste arquivo log_subdir = os.path.join(LOG_DIR, os.path.splitext(file.filename)[0]) os.makedirs(log_subdir, exist_ok=True) - # Copiar o arquivo enviado para a pasta de logs + # Copiar o arquivo enviado para os logs shutil.copy(cov_file, os.path.join(log_subdir, file.filename)) - # Pré-processamento + # Pré-processamento do arquivo min_size = 2500000 max_size = 3500000 step = 100 - - # Processar arquivo de cobertura para matriz de entrada cov_matrix = process_new_sample(cov_file, chr_arms, min_size, max_size, step) - # Preparar a matriz para predição + # Preparar matriz para predição prepared_matrix = prepare_data([cov_matrix])[0] - # Predição + # Realizar predição com o modelo prediction = model.predict(np.array([prepared_matrix]))[0][0] prediction_result = "Positive" if prediction >= threshold else "Negative" - # Gerar heatmap + # Gerar heatmap do arquivo corrected_df = pd.read_csv(cov_file, sep="\t") smoothed_cov = smooth_normalized_coverage(corrected_df, chr_arms, min_size, max_size, step) cov_matrix = create_2d_array(smoothed_cov) heatmap_buffer = plot_heatmap(cov_matrix, file.filename.split('.')[0]) - # Salvar heatmap na pasta de logs + # Salvar heatmap nos logs heatmap_path = os.path.join(log_subdir, f"{os.path.splitext(file.filename)[0]}_heatmap.png") with open(heatmap_path, "wb") as heatmap_file: heatmap_buffer.seek(0) @@ -86,17 +99,18 @@ async def process_multiple_files(files: list[UploadFile] = File(...)): heatmap_buffer.seek(0) heatmap_base64 = base64.b64encode(heatmap_buffer.read()).decode("utf-8") - # Adicionar resultado ao retorno + # Adicionar resultados ao retorno results.append({ "filename": file.filename, "prediction": prediction_result, "heatmap": heatmap_base64, }) finally: - # Remover arquivo temporário + # Remover o arquivo temporário os.remove(cov_file) - # Retornar resultados + # Retornar todos os resultados return JSONResponse(content={"results": results}) except Exception as e: + # Em caso de erro, retornar uma exceção HTTP raise HTTPException(status_code=500, detail=f"Erro ao processar arquivos: {str(e)}") diff --git a/back/requirements.txt b/back/requirements.txt index 0d1f7c326a33bfd783a4f960d149bc8967a4bb38..e73551230c70948ad10a3dcb9f4e147996eae5ab 100644 GIT binary patch delta 10 ScmZ1=_(NdAqK&7nu>$}cas@;H delta 44 ucmew%ut0FaBE?jOB8C!%OolWdwgo~X1_K~AVbEi+0AgbXgN?KAvI7A3g9-Tn diff --git a/back/utils.py b/back/utils.py index f293c22..c741209 100644 --- a/back/utils.py +++ b/back/utils.py @@ -2,6 +2,19 @@ import pandas as pd def choose_best_window_size(length, min_size, max_size, step): + """ + Escolhe o melhor tamanho de janela para dividir o comprimento de um cromossomo + de forma a minimizar o resto na divisão. + + Parâmetros: + - length (int): Comprimento total do cromossomo. + - min_size (int): Tamanho mínimo da janela. + - max_size (int): Tamanho máximo da janela. + - step (int): Incremento no tamanho da janela. + + Retorna: + - int: O tamanho de janela que minimiza o resto da divisão. + """ best_remainder = float('inf') best_size = min_size for window_size in range(min_size, max_size + step, step): @@ -12,38 +25,83 @@ def choose_best_window_size(length, min_size, max_size, step): return best_size def smooth_normalized_coverage(df, arm_lengths, min_size, max_size, step): + """ + Suaviza a cobertura normalizada para cada braço cromossômico utilizando uma janela + de tamanho ideal. + + Parâmetros: + - df (pd.DataFrame): DataFrame contendo as informações de cobertura corrigida. + - arm_lengths (dict): Dicionário com os comprimentos dos braços cromossômicos (p e q). + - min_size (int): Tamanho mínimo da janela. + - max_size (int): Tamanho máximo da janela. + - step (int): Incremento no tamanho da janela. + + Retorna: + - dict: Dicionário contendo os valores suavizados de cobertura para cada braço. + """ smoothed_cov_dict = {} for arm, length in arm_lengths.items(): + # Determina o melhor tamanho de janela para o braço cromossômico window_size = choose_best_window_size(length, min_size, max_size, step) - arm_data = df[df['chrom'] == int(arm[0])] + arm_data = df[df['chrom'] == int(arm[0])] # Filtra os dados para o cromossomo específico if not arm_data.empty: smoothed_cov = [] for start in range(0, length, window_size): end = start + window_size + # Seleciona os dados dentro da janela atual window_data = arm_data[(arm_data['start'] >= start) & (arm_data['end'] <= end)] if not window_data.empty: + # Calcula a mediana da cobertura corrigida dentro da janela median_cov = window_data['corrected_cov'].median() smoothed_cov.append(median_cov) else: - smoothed_cov.append(np.nan) + smoothed_cov.append(np.nan) # Caso não haja dados, insere NaN if len(smoothed_cov) > 0 and (length % window_size) != 0: - smoothed_cov = smoothed_cov[:-1] + smoothed_cov = smoothed_cov[:-1] # Remove a última janela se incompleta smoothed_cov_dict[arm] = smoothed_cov return smoothed_cov_dict def create_2d_array(smoothed_cov_dict): - max_bins = max(len(smoothed_cov_dict[arm]) for arm in smoothed_cov_dict) - input_matrix = np.full((22, max_bins * 2), np.nan) - for i in range(1, 23): - p_arm = f"{i}p" - q_arm = f"{i}q" + """ + Cria uma matriz 2D de cobertura a partir dos valores suavizados para todos os cromossomos. + + Parâmetros: + - smoothed_cov_dict (dict): Dicionário contendo os valores suavizados de cobertura para cada braço. + + Retorna: + - np.ndarray: Matriz 2D representando os valores de cobertura para cada cromossomo. + """ + max_bins = max(len(smoothed_cov_dict[arm]) for arm in smoothed_cov_dict) # Número máximo de bins + input_matrix = np.full((22, max_bins * 2), np.nan) # Matriz inicial preenchida com NaN + for i in range(1, 23): # Para cada cromossomo de 1 a 22 + p_arm = f"{i}p" # Nome do braço p + q_arm = f"{i}q" # Nome do braço q if p_arm in smoothed_cov_dict and q_arm in smoothed_cov_dict: + # Preenche os valores do braço p input_matrix[i-1, max_bins - len(smoothed_cov_dict[p_arm]):max_bins] = smoothed_cov_dict[p_arm] + # Preenche os valores do braço q input_matrix[i-1, max_bins:max_bins + len(smoothed_cov_dict[q_arm])] = smoothed_cov_dict[q_arm] return input_matrix def process_new_sample(file_content, chr_arms, min_size, max_size, step): + """ + Processa um arquivo de cobertura para gerar uma matriz 2D de entrada para o modelo. + + Parâmetros: + - file_content (str): Caminho para o arquivo de cobertura. + - chr_arms (dict): Dicionário com os comprimentos dos braços cromossômicos. + - min_size (int): Tamanho mínimo da janela. + - max_size (int): Tamanho máximo da janela. + - step (int): Incremento no tamanho da janela. + + Retorna: + - np.ndarray: Matriz 2D representando os valores de cobertura normalizados e suavizados. + """ + # Lê o arquivo de cobertura como um DataFrame corrected_df = pd.read_csv(file_content, sep='\t') + # Suaviza a cobertura normalizada smoothed_cov = smooth_normalized_coverage(corrected_df, chr_arms, min_size, max_size, step) + # Cria a matriz 2D cov_matrix = create_2d_array(smoothed_cov) - return np.nan_to_num(cov_matrix) \ No newline at end of file + # Substitui NaN por zeros + return np.nan_to_num(cov_matrix) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index f21943a..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -dvc==3.56.0 -dvc_s3==3.2.0