Spaces:
Sleeping
Sleeping
| 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 | |
| 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} | |