From 66084034f8f2e594eacd2d4470fc46f8b7e7a2ba Mon Sep 17 00:00:00 2001 From: caiquerdon Date: Mon, 2 Jun 2025 19:39:30 -0300 Subject: [PATCH 1/2] Fix READ.ME --- TECH-CHALLENGE-1/README.md | 147 ++++++++++++++++++++++++++----------- 1 file changed, 103 insertions(+), 44 deletions(-) diff --git a/TECH-CHALLENGE-1/README.md b/TECH-CHALLENGE-1/README.md index ca156580..ecd22511 100644 --- a/TECH-CHALLENGE-1/README.md +++ b/TECH-CHALLENGE-1/README.md @@ -1,92 +1,151 @@ +# 🟣 API Embrapa – Dados Vitivinícolas -# API Embrapa - Produção Vitivinícola 🍇 +Esta aplicação disponibiliza uma API pública para consulta dos dados vitivinícolas da Embrapa, incluindo produção nacional, importações e exportações de uvas, vinhos, sucos e derivados. -Este projeto faz parte do Tech Challenge da Pós-Graduação em Engenharia de Machine Learning. +## 🔗 Acesse a API +Acesse a API e sua documentação interativa hospedada gratuitamente no Render: -## 📌 Objetivo +👉 https://api-embrapa-28xn.onrender.com -Criar uma API REST em Python que consome dados públicos do site da Embrapa (VitiBrasil) e os disponibiliza em formato JSON, com documentação automática via Swagger. -## 🚀 Funcionalidades +--- -- Consulta dos dados da aba **Produção** diretamente do CSV oficial da Embrapa. -- API REST com Flask. -- Documentação via Swagger (Flask-RESTX). -- Estrutura pronta para expansão para outras abas (Processamento, Comercialização, etc). +## 🛠 Tecnologias Utilizadas + +- **Python 3.9 ou superior+** +- **Flask** +- **Flask-RESTx** +- **Pandas** +- **Requests** +- **Gunicorn** (para deploy no Render) + +--- -## 🔧 Tecnologias +## 📌 Funcionalidades da API -- Python 3.9+ -- Flask -- Flask-RESTX -- Pandas -- Requests +- Consulta dos dados da aba **Produção** diretamente do CSV oficial da Embrapa. +- Consulta dos dados da aba **Processamento** diretamente do CSV oficial da Embrapa. +- Consulta dos dados da aba **Comercialização** diretamente do CSV oficial da Embrapa. +- Consulta dos dados da aba **Importação** diretamente do CSV oficial da Embrapa. +- Consulta dos dados da aba **Exportação** diretamente do CSV oficial da Embrapa. +- API REST construída com **Flask** e documentada com **Flask-RESTX**. +- Retorno em formato JSON e interface interativa via Swagger UI. -## 📂 Endpoints +--- -### `GET /producao/` +### 🔹 Listar Categorias +``` +GET /categorias +``` -Retorna todos os dados da aba Produção da Embrapa. +### 🔹 Obter todos os dados de uma categoria +``` +GET /dados/categoria/ +``` -### `GET /producao/` +## 📄 Exemplo de Retorno + +### Exemplo `/dados/categoria/producao` +```json +[ + { + "Ano": 2020, + "Região": "Serra Gaúcha", + "Quantidade (t)": 123456 + } +] +``` -Retorna uma linha específica pelo índice (inteiro). +--- -### `GET /` +## 🖥️ Página Inicial -Mensagem de boas-vindas e link para a documentação. +A rota `/` exibe uma página HTML simples e links diretos para as categorias, além do link para a documentação Swagger. -## 📄 Documentação Swagger +--- -Disponível automaticamente em: +## 🚀 Deploy no Render +### Estrutura esperada: ``` -http://localhost:5000/ +. +├── app.py +├── requirements.txt +└── Procfile ``` -## ▶️ Como executar localmente +### Exemplo de `Procfile` +```txt +web: gunicorn app:app +``` + +--- + +## ▶️ Como Executar Localmente -1. Clone o repositório: +### 1. Clone o repositório: ```bash -git clone https://github.com/seuusuario/tech-challenge-embrapa.git -cd tech-challenge-embrapa +git clone https://github.com/caiquerdon/machinelearningengineering.git +cd TECH-CHALLENGE-1 ``` -2. Crie e ative um ambiente virtual: +### 2. (Opcional) Crie um ambiente virtual: ```bash python3 -m venv venv -source venv/bin/activate +source venv/bin/activate # Linux/macOS +venv\Scripts\activate # Windows ``` -3. Instale as dependências: +### 3. Instale as dependências: ```bash +cd src pip install -r requirements.txt ``` -4. Execute o app: +### 4. Execute a aplicação: ```bash python app.py ``` -## 🔐 Segurança +Acesse http://localhost:5000 no navegador. + +--- + +## 📁 Estrutura de Diretórios + +``` +. +├── app.py # Código principal da API +├── requirements.txt # Dependências do projeto +├── Procfile # Arquivo de execução para o Render +├── README.md # Este arquivo :) +└── src/ # Scripts e utilitários adicionais +``` -A API atualmente está pública. Para produção, recomenda-se o uso de autenticação via JWT ou chave de API. +--- -## 🧠 Possível uso em ML +## ⚙️ Sobre o Procfile -Os dados obtidos por essa API podem ser usados para: -- Previsão de produção por estado ou tipo de uva -- Análise de sazonalidade -- Estudos de exportação/importação de vinho +O `Procfile` é um arquivo usado por plataformas como o Render para saber como iniciar a aplicação. No nosso caso, ele contém: -## 📌 Desafio proposto +```txt +web: gunicorn app:app +``` + +Isso instrui o Render a utilizar o Gunicorn para rodar a aplicação Flask a partir do arquivo `app.py`. + +--- + +## 🔐 Segurança -Este projeto é parte do desafio da Fase 1 da Pós-Graduação em Engenharia de Machine Learning e representa 60% da nota da fase. +Atualmente, a API está pública. Para ambientes de produção, recomenda-se implementar autenticação via JWT, OAuth2 ou chaves de API. -## 🧑‍💻 Autor +--- +## 👨‍💻 Autores -Matheus Pavani +- **Caique Nascimento** +- **Gustavo Carrillo** \ No newline at end of file From 261bdb1a22411c2c064744140a506cad8d5cc060 Mon Sep 17 00:00:00 2001 From: caiquerdon Date: Mon, 2 Jun 2025 21:01:20 -0300 Subject: [PATCH 2/2] Ajuste final repositorio --- .DS_Store | Bin 6148 -> 6148 bytes .gitignore | 8 +++++++- TECH-CHALLENGE-1/README.md | 4 +++- TECH-CHALLENGE-1/src/app.py | 18 ++---------------- swagger.png | Bin 7109 -> 0 bytes 5 files changed, 12 insertions(+), 18 deletions(-) delete mode 100644 swagger.png diff --git a/.DS_Store b/.DS_Store index 41195ebf70dea5467ce5b5f4bdb9e4b8d34ea24e..1d1a78c97b9af657c2e6b38ba5354c0820b33345 100644 GIT binary patch delta 32 ocmZoMXfc@J&nUPtU^g?P;AS3{QpU~OSp%3R7MN{j=lIJH0H)UpDgXcg delta 69 zcmZoMXfc@J&nUDpU^g?P&}JT%Qbu)dhGK?thD3&RAWUT_V$fqKV8~-g_sq#pPRhwo XVqjnpU|?YE-dw~Q&$OAH<1aq|S!xj$ diff --git a/.gitignore b/.gitignore index eba74f4c..d5f8494b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,7 @@ -venv/ \ No newline at end of file +venv/ +lambda_function.zip +lambda_app.py +lambda_build +api_embrapa_lambda.zip +build.sh +embrapa_api_jwt.py diff --git a/TECH-CHALLENGE-1/README.md b/TECH-CHALLENGE-1/README.md index ecd22511..860826c2 100644 --- a/TECH-CHALLENGE-1/README.md +++ b/TECH-CHALLENGE-1/README.md @@ -1,4 +1,4 @@ -# 🟣 API Embrapa – Dados Vitivinícolas +# API Embrapa – Dados Vitivinícolas Esta aplicação disponibiliza uma API pública para consulta dos dados vitivinícolas da Embrapa, incluindo produção nacional, importações e exportações de uvas, vinhos, sucos e derivados. @@ -7,6 +7,8 @@ Acesse a API e sua documentação interativa hospedada gratuitamente no Render: 👉 https://api-embrapa-28xn.onrender.com +## ▶️ Vídeo demonstrativo do Deploy : +https://www.youtube.com/watch?v=AEXQBhZ0rB0&feature=youtu.be --- diff --git a/TECH-CHALLENGE-1/src/app.py b/TECH-CHALLENGE-1/src/app.py index 9117a4b1..9a4863eb 100644 --- a/TECH-CHALLENGE-1/src/app.py +++ b/TECH-CHALLENGE-1/src/app.py @@ -21,7 +21,7 @@ # Configuração do namespace da API ns = api.namespace('dados', description='Operações com os dados vitivinícolas') -# CSV +# URLs dos arquivos CSV disponíveis na Embrapa CSV_URLS = { 'producao': 'http://vitibrasil.cnpuv.embrapa.br/download/Producao.csv', 'processa_viniferas': 'http://vitibrasil.cnpuv.embrapa.br/download/ProcessaViniferas.csv', @@ -40,7 +40,7 @@ 'exp_suco': 'http://vitibrasil.cnpuv.embrapa.br/download/ExpSuco.csv', } -# +# Configuração dos parâmetros de leitura dos arquivos CSV CSV_CONFIGS = { 'producao': {'sep': ';', 'encoding': 'latin1'}, 'processa_viniferas': {'sep': ';', 'encoding': 'latin1'}, @@ -107,20 +107,6 @@ def get(self): resultado[cat] = f"Erro ao carregar dados da categoria: {cat}" return jsonify(resultado) -# Configuração do endpoint para obter uma linha específica de uma categoria -# @ns.route('//') -# @ns.param('categoria', 'Nome da categoria desejada') -# @ns.param('linha', 'Índice da linha desejada') -# class LinhaEspecifica(Resource): -# def get(self, categoria, linha): -# """Retorna os dados de uma linha específica""" -# df = carregar_dados(categoria) -# if df is None or df.empty: -# return {'erro': 'Categoria inválida ou erro ao carregar dados'}, 404 -# if linha < 0 or linha >= len(df): -# return {'erro': 'Índice fora do intervalo'}, 400 -# return jsonify(df.iloc[linha].to_dict()) - # Configuração do endpoint para listar todas as categorias disponíveis @api.route('/categorias') class ListaCategorias(Resource): diff --git a/swagger.png b/swagger.png deleted file mode 100644 index e0be801ac5a97f087b9d22829a0ac9fd672826a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7109 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