import pandas as pd from itertools import permutations from fastapi import FastAPI, File, UploadFile, Form, HTTPException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from typing import List app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=["*"], # Allows all origins allow_credentials=True, allow_methods=["*"], # Allows all methods allow_headers=["*"], # Allows all headers ) class Box: def __init__(self, length, width, height, quantity, box_type): self.length = length self.width = width self.height = height self.quantity = quantity if quantity > 0 else 1 # Ensure at least 1 box if quantity is not specified self.type = box_type def rotated_dimensions(self): # Return rotations that maximize base area and minimize height possible_rotations = [ (self.length, self.width, self.height), (self.length, self.height, self.width), (self.width, self.length, self.height), (self.width, self.height, self.length), (self.height, self.length, self.width), (self.height, self.width, self.length) ] # Sort by volume and base area to prioritize the best fit return sorted(possible_rotations, key=lambda x: (x[0] * x[1], x[2]), reverse=True) def volume(self): return self.length * self.width * self.height class Truck: def __init__(self, length, width, height): self.length = length self.width = width self.height = height self.volume = length * width * height self.placed_boxes = [] self.available_space = [(0, 0, 0, length, width, height)] # (x, y, z, l, w, h) def add_box(self, box): best_fit = None best_fit_space_index = -1 for rotation in box.rotated_dimensions(): for i, space in enumerate(self.available_space): x, y, z, l, w, h = space # Check if the box can fit in the current space if rotation[0] <= l and rotation[1] <= w and rotation[2] <= h: if not best_fit or (rotation[0] * rotation[1] > best_fit[0] * best_fit[1]): best_fit = rotation best_fit_space_index = i if best_fit: x, y, z, l, w, h = self.available_space[best_fit_space_index] box_position = (x, y, z) # Place the box in the truck self.placed_boxes.append((best_fit, box_position)) # Update available space after placing the box self.available_space.pop(best_fit_space_index) self.update_space(best_fit, box_position, l, w, h) return box_position else: return None def update_space(self, box, position, l, w, h): x, y, z = position bl, bw, bh = box # Create new spaces based on the placement of the new box new_spaces = [ (x + bl, y, z, l - bl, w, h), # Space to the right (x, y + bw, z, bl, w - bw, h), # Space in front (x, y, z + bh, bl, bw, h - bh), # Space above ] # Filter out invalid spaces new_spaces = [space for space in new_spaces if space[3] > 0 and space[4] > 0 and space[5] > 0] # Add new valid spaces to the available_space list self.available_space.extend(new_spaces) # Sort available spaces to prioritize lower and more central spaces for stacking self.available_space.sort(key=lambda space: (space[2], space[1], space[0])) def pack_boxes(truck, boxes): packed_positions = [] # Sort all boxes by volume (largest first) boxes.sort(key=lambda b: b.volume(), reverse=True) # Pack all boxes, ensuring practical stacking for box in boxes: for _ in range(box.quantity): position = truck.add_box(box) if position is None: break packed_positions.append((box, position)) return packed_positions @app.post("/upload/") async def upload_file( file: UploadFile = File(...), length: float = Form(...), width: float = Form(...), height: float = Form(...), ): if not file: raise HTTPException(status_code=400, detail="No file uploaded") ext = file.filename.split('.')[-1].lower() if ext == 'csv': data = pd.read_csv(file.file) elif ext in ['xls', 'xlsx']: data = pd.read_excel(file.file) else: raise HTTPException(status_code=400, detail="Unsupported file format") # Convert dimensions from CM to inches data['PieceLength'] = data['PieceLength'] / 2.54 data['PieceBreadth'] = data['PieceBreadth'] / 2.54 data['PieceHeight'] = data['PieceHeight'] / 2.54 # Create Box objects with quantity considered boxes = [ Box(row['PieceLength'], row['PieceBreadth'], row['PieceHeight'], row.get('Quantity', 1), f"{row['PieceNo']}-{row['Priority']}") for _, row in data.iterrows() ] # Convert truck dimensions from feet to inches truck_length = length * 12 # Convert to inches truck_width = width * 12 # Convert to inches truck_height = height * 12 # Convert to inches truck = Truck(truck_length, truck_width, truck_height) # Pack the boxes into the truck packed_positions = pack_boxes(truck, boxes) box_data = [ { 'length': box.length, 'width': box.width, 'height': box.height, 'type': box.type, 'quantity': box.quantity, 'position': {'x': pos[0], 'y': pos[1], 'z': pos[2]} } for box, pos in packed_positions ] print(f"quantity {[box_data[i]['quantity'] for i in range(len(box_data))]}") return {"boxes": box_data}