diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000..ecf0bf5 Binary files /dev/null and b/__pycache__/main.cpython-312.pyc differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..9db773f --- /dev/null +++ b/main.py @@ -0,0 +1,191 @@ +from fastapi import FastAPI, UploadFile, File, HTTPException, Request, Form +from fastapi.responses import FileResponse, HTMLResponse +from fastapi.staticfiles import StaticFiles +from fastapi.templating import Jinja2Templates +import pandas as pd +import json +import os +from datetime import datetime +from typing import Dict, Any +from openpyxl import load_workbook +from openpyxl.styles import PatternFill, Font, Alignment, Border, Side +from openpyxl.worksheet.table import Table, TableStyleInfo + +app = FastAPI( + title="API Conversor JSON para Excel", + description="API para converter arquivos JSON em Excel", + version="1.0.0" +) + +# Configurar arquivos estáticos e templates +app.mount("/static", StaticFiles(directory="static"), name="static") +templates = Jinja2Templates(directory="templates") + +# Rota principal - renderiza o template HTML +@app.get("/", response_class=HTMLResponse) +async def home(request: Request): + return templates.TemplateResponse("index.html", {"request": request}) + +@app.post("/convert/file/") +async def convert_file(json_file: UploadFile = File(...)): + """ + Converte um arquivo JSON enviado para Excel + """ + if not json_file.filename.endswith(".json"): + raise HTTPException(status_code=400, detail="Por favor, envie um arquivo JSON.") + + try: + content = await json_file.read() + json_data = json.loads(content.decode('utf-8')) + return await convert_json_to_excel(json_data, json_file.filename) + + except json.JSONDecodeError: + raise HTTPException(status_code=400, detail="Arquivo JSON inválido") + except Exception as e: + raise HTTPException(status_code=500, detail=f"Erro ao processar arquivo: {str(e)}") + +@app.post("/convert/json/") +async def convert_json(json_text: str = Form(...)): + """ + Converte JSON enviado diretamente no body para Excel + """ + try: + # Tenta fazer o parse do JSON + json_data = json.loads(json_text) + + # Verifica se o JSON é um dicionário ou lista + if not isinstance(json_data, (dict, list)): + raise HTTPException(status_code=400, detail="O JSON deve ser um objeto ou array") + + # Se for uma lista de objetos, converte diretamente + if isinstance(json_data, list): + df = pd.DataFrame(json_data) + # Se for um objeto único, converte para lista com um item + else: + df = pd.DataFrame([json_data]) + + # Cria nome único para o arquivo Excel + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + excel_filename = f"uploads/json_data_{timestamp}.xlsx" + + # Cria diretório se não existir + os.makedirs("uploads", exist_ok=True) + + # Salva como Excel + df.to_excel(excel_filename, index=False) + + # Retorna o arquivo Excel + return FileResponse( + path=excel_filename, + filename=os.path.basename(excel_filename), + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + ) + + except json.JSONDecodeError: + raise HTTPException(status_code=400, detail="JSON inválido") + except Exception as e: + raise HTTPException(status_code=500, detail=f"Erro ao processar JSON: {str(e)}") + +async def convert_json_to_excel(json_data: dict, original_filename: str): + """ + Função auxiliar para converter JSON em Excel com formatação adequada + """ + try: + # Converte JSON para DataFrame + if isinstance(json_data, list): + df = pd.DataFrame(json_data) + else: + df = pd.DataFrame([json_data]) + + # Cria nome único para o arquivo Excel + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + excel_filename = f"uploads/{os.path.splitext(original_filename)[0]}_{timestamp}.xlsx" + + # Cria diretório se não existir + os.makedirs("uploads", exist_ok=True) + + # Salva primeiro sem índice + df.to_excel(excel_filename, index=False) + + # Carrega o workbook para formatação + wb = load_workbook(excel_filename) + ws = wb.active + + # Define estilos + header_fill = PatternFill(start_color="1F4E78", end_color="1F4E78", fill_type="solid") + header_font = Font(name='Arial', size=11, color="FFFFFF", bold=True) + cell_font = Font(name='Arial', size=10) + + # Aplica estilos ao cabeçalho + for cell in ws[1]: + cell.fill = header_fill + cell.font = header_font + cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) + + # Aplica estilos às células de dados + for row in ws.iter_rows(min_row=2): + for cell in row: + cell.font = cell_font + cell.alignment = Alignment(horizontal='left', vertical='center', wrap_text=True) + cell.border = Border( + left=Side(style='thin', color='D4D4D4'), + right=Side(style='thin', color='D4D4D4'), + top=Side(style='thin', color='D4D4D4'), + bottom=Side(style='thin', color='D4D4D4') + ) + + # Ajusta largura das colunas + for column in ws.columns: + max_length = 0 + column_letter = column[0].column_letter + + for cell in column: + try: + if len(str(cell.value)) > max_length: + max_length = len(str(cell.value)) + except: + pass + + adjusted_width = min(max_length + 2, 50) # Limita a largura máxima + ws.column_dimensions[column_letter].width = adjusted_width + + # Remove tabela existente se houver + if len(ws.tables) > 0: + del ws.tables[ws.tables.keys()[0]] + + # Cria uma nova tabela + table_name = f"Table_{timestamp}" # Use uma variável timestamp para nome único + tab = Table(displayName=table_name, ref=ws.dimensions) + + # Aplica estilo à tabela + style = TableStyleInfo( + name="TableStyleMedium2", + showFirstColumn=False, + showLastColumn=False, + showRowStripes=True, + showColumnStripes=False + ) + tab.tableStyleInfo = style + + # Adiciona a tabela + ws.add_table(tab) + + # Congela o painel + ws.freeze_panes = 'A2' + + # Salva as alterações + wb.save(excel_filename) + + # Retorna o arquivo Excel + return FileResponse( + path=excel_filename, + filename=os.path.basename(excel_filename), + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + ) + + except Exception as e: + raise HTTPException(status_code=500, detail=f"Erro ao formatar Excel: {str(e)}") + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..e03b008 --- /dev/null +++ b/static/style.css @@ -0,0 +1,125 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + background-color: #f5f5f5; + color: #333; + line-height: 1.6; +} + +.container { + max-width: 800px; + margin: 2rem auto; + padding: 0 1rem; +} + +h1 { + text-align: center; + color: #2c3e50; + margin-bottom: 2rem; +} + +.converter-box { + background: white; + border-radius: 10px; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + padding: 2rem; +} + +.tab-container { + display: flex; + margin-bottom: 1rem; + border-bottom: 2px solid #eee; +} + +.tab-button { + padding: 0.5rem 1rem; + border: none; + background: none; + cursor: pointer; + font-size: 1rem; + color: #666; + transition: all 0.3s ease; +} + +.tab-button.active { + color: #2980b9; + border-bottom: 2px solid #2980b9; + margin-bottom: -2px; +} + +.tab-content { + display: none; +} + +.tab-content.active { + display: block; +} + +.upload-form { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.drop-zone { + border: 2px dashed #ccc; + border-radius: 5px; + padding: 2rem; + text-align: center; + cursor: pointer; + transition: border 0.3s ease; +} + +.drop-zone:hover { + border-color: #2980b9; +} + +.drop-zone-input { + display: none; +} + +.json-input { + width: 100%; + height: 200px; + padding: 1rem; + border: 1px solid #ccc; + border-radius: 5px; + resize: vertical; + font-family: monospace; +} + +.convert-button { + background-color: #2980b9; + color: white; + border: none; + padding: 1rem; + border-radius: 5px; + cursor: pointer; + font-size: 1rem; + transition: background-color 0.3s ease; +} + +.convert-button:hover { + background-color: #2471a3; +} + +.drop-zone-hover { + border-color: #2980b9; + background-color: #2980b91a; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..7e048a3 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,122 @@ + + + + + + Conversor JSON para Excel + + + +
+

Conversor JSON para Excel

+
+
+ + +
+ +
+
+
+

Arraste e solte seu arquivo JSON aqui ou clique para selecionar

+ + +
+ +
+
+ +
+
+ + +
+
+
+
+ + + + diff --git a/uploads/json_data_20241114_100911.xlsx b/uploads/json_data_20241114_100911.xlsx new file mode 100644 index 0000000..9ef58ae Binary files /dev/null and b/uploads/json_data_20241114_100911.xlsx differ diff --git a/uploads/json_data_20241114_101322.xlsx b/uploads/json_data_20241114_101322.xlsx new file mode 100644 index 0000000..e303b4f Binary files /dev/null and b/uploads/json_data_20241114_101322.xlsx differ diff --git a/uploads/json_data_20241114_101526.xlsx b/uploads/json_data_20241114_101526.xlsx new file mode 100644 index 0000000..081e3ec Binary files /dev/null and b/uploads/json_data_20241114_101526.xlsx differ diff --git a/uploads/json_data_20241114_114320.xlsx b/uploads/json_data_20241114_114320.xlsx new file mode 100644 index 0000000..42e6eec Binary files /dev/null and b/uploads/json_data_20241114_114320.xlsx differ diff --git a/uploads/package_20241114_101643.xlsx b/uploads/package_20241114_101643.xlsx new file mode 100644 index 0000000..c52c0b8 Binary files /dev/null and b/uploads/package_20241114_101643.xlsx differ diff --git a/uploads/translation_20241114_100934.xlsx b/uploads/translation_20241114_100934.xlsx new file mode 100644 index 0000000..287b881 Binary files /dev/null and b/uploads/translation_20241114_100934.xlsx differ diff --git a/uploads/translation_20241114_101300.xlsx b/uploads/translation_20241114_101300.xlsx new file mode 100644 index 0000000..71ec563 Binary files /dev/null and b/uploads/translation_20241114_101300.xlsx differ