from fastapi import FastAPI, UploadFile, File, Request, Form from fastapi.responses import HTMLResponse, FileResponse from fastapi.templating import Jinja2Templates from fastapi.staticfiles import StaticFiles import pandas as pd import json from pathlib import Path import shutil from typing import List app = FastAPI(title="JSON Price Converter") # Create folders UPLOAD_DIR = Path("uploads") UPLOAD_DIR.mkdir(exist_ok=True) # Templates templates = Jinja2Templates(directory="templates") # Mount static if needed (optional) # app.mount("/static", StaticFiles(directory="static"), name="static") def extract_price_from_json(data: dict, filename: str) -> list: price_records = [] product_name = data.get('name', [{}])[0].get('value', 'Unknown Product') product_id = data.get('id') currency = data.get('currency') price_type = data.get('priceType') external_id = data.get('externalId', [{}])[0].get('id') if data.get('externalId') else None description = data.get('description', [{}])[0].get('value', '') for record in data.get('priceRecord', []): params = {p['name']: p['value'] for p in record.get('parameter', [])} for price_item in record.get('price', []): row = { 'filename': filename, 'product_id': product_id, 'product_name': product_name, 'description': description, 'currency': currency, 'price_type': price_type, 'tier_counter': params.get('TIERED_PRICING_COUNTER'), 'sales_channel': params.get('SalesChannel'), 'amount_idr': price_item.get('taxIncludedAmount', {}).get('value'), 'valid_from': price_item.get('validFor', {}).get('startDateTime'), 'valid_until': price_item.get('validFor', {}).get('endDateTime'), 'price_record_id': record.get('priceRecordId'), 'priority': record.get('priority'), 'external_id': external_id, } price_records.append(row) return price_records @app.get("/", response_class=HTMLResponse) async def home(request: Request): return templates.TemplateResponse("index.html", {"request": request}) @app.post("/upload") async def upload_files(files: List[UploadFile] = File(...)): all_rows = [] for file in files: if not file.filename.endswith('.json'): continue content = await file.read() try: data = json.loads(content) rows = extract_price_from_json(data, file.filename) all_rows.extend(rows) except Exception as e: print(f"Error processing {file.filename}: {e}") if not all_rows: return {"error": "No valid JSON files processed"} df = pd.DataFrame(all_rows) # Save results csv_path = UPLOAD_DIR / "all_prices_table.csv" excel_path = UPLOAD_DIR / "all_prices_table.xlsx" df.to_csv(csv_path, index=False) df.to_excel(excel_path, index=False) return { "message": f"Successfully processed {len(files)} files", "total_rows": len(df), "download_csv": "/download/csv", "download_excel": "/download/excel", "preview": df.head(10).to_dict(orient="records") # for display } @app.get("/download/csv") async def download_csv(): file_path = UPLOAD_DIR / "all_prices_table.csv" if not file_path.exists(): return {"error": "No file available"} return FileResponse(file_path, media_type="text/csv", filename="all_prices_table.csv") @app.get("/download/excel") async def download_excel(): file_path = UPLOAD_DIR / "all_prices_table.xlsx" if not file_path.exists(): return {"error": "No file available"} return FileResponse(file_path, media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", filename="all_prices_table.xlsx") # Run with: uvicorn main:app --reload if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)