Do0rMaMu's picture
Update app.py
463d47b verified
raw
history blame
9.46 kB
import pandas as pd
from itertools import permutations
from fastapi import FastAPI, File, UploadFile, Form, HTTPException, Request, Response
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=["*"],
allow_headers=["*"],
)
@app.options("/{rest_of_path:path}")
async def preflight_handler(request: Request, rest_of_path: str) -> Response:
return Response(headers={
"Access-Control-Allow-Origin": "*", # Allows all origins
"Access-Control-Allow-Methods": "POST, GET, OPTIONS, PUT, DELETE", # Specify methods allowed
"Access-Control-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
def suggest_truck(boxes):
trucks = {
"TATA ACE": {"length": 7, "width": 4.8, "height": 4.8},
"ASHOK LEYLAND DOST": {"length": 7, "width": 4.8, "height": 4.8},
"MAHINDRA BOLERO PICK UP": {"length": 8, "width": 5, "height": 4.8},
"ASHOK LEYLAND BADA DOST": {"length": 9.5, "width": 5.5, "height": 5},
"TATA 407": {"length": 9, "width": 5.5, "height": 5},
"EICHER 14 FEET": {"length": 14, "width": 6, "height": 6.5},
"EICHER 17 FEET": {"length": 17, "width": 6, "height": 7},
"EICHER 19 FEET": {"length": 19, "width": 7, "height": 7},
"TATA 22 FEET": {"length": 22, "width": 7.5, "height": 7},
"TATA TRUCK (6 TYRE)": {"length": 17.5, "width": 7, "height": 7},
"TAURUS 16 T (10 TYRE)": {"length": 21, "width": 7.2, "height": 7},
"TAURUS 21 T (12 TYRE)": {"length": 24, "width": 7.3, "height": 7},
"TAURUS 25 T (14 TYRE)": {"length": 28, "width": 7.8, "height": 7},
"CONTAINER 20 FT": {"length": 20, "width": 8, "height": 8},
"CONTAINER 32 FT SXL": {"length": 32, "width": 8, "height": 8},
"CONTAINER 32 FT MXL": {"length": 32, "width": 8, "height": 8},
"CONTAINER 32 FT SXL / MXL HQ": {"length": 32, "width": 8, "height": 10},
"20 FEET OPEN ALL SIDE (ODC)": {"length": 20, "width": 8, "height": 8},
"28-32 FEET OPEN-TRAILOR JCB ODC": {"length": 28, "width": 8, "height": 8},
"32 FEET OPEN-TRAILOR ODC": {"length": 32, "width": 8, "height": 8},
"40 FEET OPEN-TRAILOR ODC": {"length": 40, "width": 8, "height": 8},
"SCV": {"length": 5, "width": 12, "height": 6},
"LCV": {"length": 11, "width": 5, "height": 5},
"ICV": {"length": 16, "width": 6.5, "height": 6.5},
"MCV": {"length": 19, "width": 7, "height": 7}
}
# Sort trucks by volume (smallest first)
sorted_trucks = sorted(trucks.items(), key=lambda t: t[1]['length'] * t[1]['width'] * t[1]['height'])
# Try packing the boxes in each truck, return the first one that fits all boxes
for truck_name, dimensions in sorted_trucks:
truck = Truck(dimensions['length'] * 12, dimensions['width'] * 12, dimensions['height'] * 12) # Convert to inches
packed_positions = pack_boxes(truck, boxes)
# Check if all boxes were successfully packed
if len(packed_positions) == sum(box.quantity for box in boxes):
return {"name": truck_name, "dimensions": dimensions}
return None
@app.post("/upload/")
async def upload_file(
file: UploadFile = File(...),
length: float = Form(None), # Make these optional
width: float = Form(None),
height: float = Form(None),
name: str = Form(None),
autoSuggest: bool = Form(False)
):
if not file:
raise HTTPException(status_code=400, detail="No file uploaded")
print("truck name ", name)
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()
]
if autoSuggest:
# Suggest a truck that can fit all the boxes
suggested_truck = suggest_truck(boxes)
if suggested_truck:
length = suggested_truck['dimensions']['length']
width = suggested_truck['dimensions']['width']
height = suggested_truck['dimensions']['height']
else:
raise HTTPException(status_code=400, detail="No suitable truck found")
# 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
]
return {
"boxes": box_data,
"truck": {
"name": suggested_truck["name"] if autoSuggest else name,
"dimensions": {
"length": length,
"width": width,
"height": height
}
}
}