Do0rMaMu commited on
Commit
21db5e9
1 Parent(s): 463d47b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +341 -89
app.py CHANGED
@@ -1,16 +1,20 @@
1
  import pandas as pd
2
- from itertools import permutations
3
  from fastapi import FastAPI, File, UploadFile, Form, HTTPException, Request, Response
4
  from fastapi.middleware.cors import CORSMiddleware
5
  from pydantic import BaseModel
6
- from typing import List
7
-
 
 
8
 
9
  app = FastAPI()
 
 
10
 
11
  app.add_middleware(
12
  CORSMiddleware,
13
- allow_origins=["*"], # Allows all origins
14
  allow_credentials=True,
15
  allow_methods=["*"],
16
  allow_headers=["*"],
@@ -19,21 +23,22 @@ app.add_middleware(
19
  @app.options("/{rest_of_path:path}")
20
  async def preflight_handler(request: Request, rest_of_path: str) -> Response:
21
  return Response(headers={
22
- "Access-Control-Allow-Origin": "*", # Allows all origins
23
- "Access-Control-Allow-Methods": "POST, GET, OPTIONS, PUT, DELETE", # Specify methods allowed
24
- "Access-Control-Allow-Headers": "*", # Allows all headers
25
  })
26
 
 
 
27
  class Box:
28
  def __init__(self, length, width, height, quantity, box_type):
29
  self.length = length
30
  self.width = width
31
  self.height = height
32
- self.quantity = quantity if quantity > 0 else 1 # Ensure at least 1 box if quantity is not specified
33
  self.type = box_type
34
 
35
  def rotated_dimensions(self):
36
- # Return rotations that maximize base area and minimize height
37
  possible_rotations = [
38
  (self.length, self.width, self.height),
39
  (self.length, self.height, self.width),
@@ -42,12 +47,12 @@ class Box:
42
  (self.height, self.length, self.width),
43
  (self.height, self.width, self.length)
44
  ]
45
- # Sort by volume and base area to prioritize the best fit
46
  return sorted(possible_rotations, key=lambda x: (x[0] * x[1], x[2]), reverse=True)
47
 
48
  def volume(self):
49
  return self.length * self.width * self.height
50
 
 
51
  class Truck:
52
  def __init__(self, length, width, height):
53
  self.length = length
@@ -55,30 +60,50 @@ class Truck:
55
  self.height = height
56
  self.volume = length * width * height
57
  self.placed_boxes = []
58
- self.available_space = [(0, 0, 0, length, width, height)] # (x, y, z, l, w, h)
 
 
 
 
 
 
 
 
 
 
 
59
 
60
- def add_box(self, box):
 
 
 
 
 
61
  best_fit = None
62
  best_fit_space_index = -1
 
63
 
64
  for rotation in box.rotated_dimensions():
65
  for i, space in enumerate(self.available_space):
66
  x, y, z, l, w, h = space
67
 
68
- # Check if the box can fit in the current space
69
  if rotation[0] <= l and rotation[1] <= w and rotation[2] <= h:
70
- if not best_fit or (rotation[0] * rotation[1] > best_fit[0] * best_fit[1]):
 
 
 
 
 
71
  best_fit = rotation
72
  best_fit_space_index = i
 
73
 
74
  if best_fit:
75
  x, y, z, l, w, h = self.available_space[best_fit_space_index]
76
  box_position = (x, y, z)
77
 
78
- # Place the box in the truck
79
  self.placed_boxes.append((best_fit, box_position))
80
 
81
- # Update available space after placing the box
82
  self.available_space.pop(best_fit_space_index)
83
  self.update_space(best_fit, box_position, l, w, h)
84
  return box_position
@@ -89,39 +114,84 @@ class Truck:
89
  x, y, z = position
90
  bl, bw, bh = box
91
 
92
- # Create new spaces based on the placement of the new box
93
  new_spaces = [
94
- (x + bl, y, z, l - bl, w, h), # Space to the right
95
- (x, y + bw, z, bl, w - bw, h), # Space in front
96
- (x, y, z + bh, bl, bw, h - bh), # Space above
97
  ]
98
 
99
- # Filter out invalid spaces
100
  new_spaces = [space for space in new_spaces if space[3] > 0 and space[4] > 0 and space[5] > 0]
101
 
102
- # Add new valid spaces to the available_space list
103
  self.available_space.extend(new_spaces)
104
-
105
- # Sort available spaces to prioritize lower and more central spaces for stacking
106
  self.available_space.sort(key=lambda space: (space[2], space[1], space[0]))
107
 
108
- def pack_boxes(truck, boxes):
 
 
109
  packed_positions = []
110
 
111
- # Sort all boxes by volume (largest first)
112
- boxes.sort(key=lambda b: b.volume(), reverse=True)
113
 
114
- # Pack all boxes, ensuring practical stacking
115
- for box in boxes:
116
- for _ in range(box.quantity):
117
- position = truck.add_box(box)
118
- if position is None:
119
- break
120
- packed_positions.append((box, position))
 
 
 
 
 
 
121
 
122
  return packed_positions
123
 
124
- def suggest_truck(boxes):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  trucks = {
126
  "TATA ACE": {"length": 7, "width": 4.8, "height": 4.8},
127
  "ASHOK LEYLAND DOST": {"length": 7, "width": 4.8, "height": 4.8},
@@ -150,72 +220,179 @@ def suggest_truck(boxes):
150
  "MCV": {"length": 19, "width": 7, "height": 7}
151
  }
152
 
153
- # Sort trucks by volume (smallest first)
154
  sorted_trucks = sorted(trucks.items(), key=lambda t: t[1]['length'] * t[1]['width'] * t[1]['height'])
155
-
156
- # Try packing the boxes in each truck, return the first one that fits all boxes
157
  for truck_name, dimensions in sorted_trucks:
158
- truck = Truck(dimensions['length'] * 12, dimensions['width'] * 12, dimensions['height'] * 12) # Convert to inches
159
- packed_positions = pack_boxes(truck, boxes)
160
-
161
- # Check if all boxes were successfully packed
162
- if len(packed_positions) == sum(box.quantity for box in boxes):
 
163
  return {"name": truck_name, "dimensions": dimensions}
164
-
165
  return None
166
 
167
- @app.post("/upload/")
168
- async def upload_file(
169
- file: UploadFile = File(...),
170
- length: float = Form(None), # Make these optional
171
- width: float = Form(None),
172
- height: float = Form(None),
173
- name: str = Form(None),
174
- autoSuggest: bool = Form(False)
175
- ):
176
- if not file:
177
- raise HTTPException(status_code=400, detail="No file uploaded")
178
-
179
- print("truck name ", name)
180
- ext = file.filename.split('.')[-1].lower()
181
- if ext == 'csv':
182
- data = pd.read_csv(file.file)
183
- elif ext in ['xls', 'xlsx']:
184
- data = pd.read_excel(file.file)
185
- else:
186
- raise HTTPException(status_code=400, detail="Unsupported file format")
187
-
188
- # Convert dimensions from CM to inches
189
- data['PieceLength'] = data['PieceLength'] / 2.54
190
- data['PieceBreadth'] = data['PieceBreadth'] / 2.54
191
- data['PieceHeight'] = data['PieceHeight'] / 2.54
192
-
193
- # Create Box objects with quantity considered
194
- boxes = [
195
- Box(row['PieceLength'], row['PieceBreadth'], row['PieceHeight'], row.get('Quantity', 1), f"{row['PieceNo']}-{row['Priority']}")
196
- for _, row in data.iterrows()
197
- ]
198
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  if autoSuggest:
200
- # Suggest a truck that can fit all the boxes
201
- suggested_truck = suggest_truck(boxes)
202
  if suggested_truck:
 
203
  length = suggested_truck['dimensions']['length']
204
  width = suggested_truck['dimensions']['width']
205
  height = suggested_truck['dimensions']['height']
206
  else:
207
  raise HTTPException(status_code=400, detail="No suitable truck found")
208
-
209
- # Convert truck dimensions from feet to inches
210
- truck_length = length * 12 # Convert to inches
211
- truck_width = width * 12 # Convert to inches
212
- truck_height = height * 12 # Convert to inches
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
- truck = Truck(truck_length, truck_width, truck_height)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
- # Pack the boxes into the truck
217
- packed_positions = pack_boxes(truck, boxes)
218
-
 
 
219
  box_data = [
220
  {
221
  'length': box.length,
@@ -228,10 +405,12 @@ async def upload_file(
228
  for box, pos in packed_positions
229
  ]
230
 
231
- return {
 
 
232
  "boxes": box_data,
233
  "truck": {
234
- "name": suggested_truck["name"] if autoSuggest else name,
235
  "dimensions": {
236
  "length": length,
237
  "width": width,
@@ -239,3 +418,76 @@ async def upload_file(
239
  }
240
  }
241
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import pandas as pd
2
+ from itertools import permutations, combinations
3
  from fastapi import FastAPI, File, UploadFile, Form, HTTPException, Request, Response
4
  from fastapi.middleware.cors import CORSMiddleware
5
  from pydantic import BaseModel
6
+ from typing import List, Dict, Any
7
+ import uuid
8
+ import requests
9
+ import json
10
 
11
  app = FastAPI()
12
+ stored_combinations = {}
13
+ processed_data_store: Dict[str, Dict[str, Any]] = {} # Store processed data with session_id
14
 
15
  app.add_middleware(
16
  CORSMiddleware,
17
+ allow_origins=["*"], # Adjust as needed for security
18
  allow_credentials=True,
19
  allow_methods=["*"],
20
  allow_headers=["*"],
 
23
  @app.options("/{rest_of_path:path}")
24
  async def preflight_handler(request: Request, rest_of_path: str) -> Response:
25
  return Response(headers={
26
+ "Access-Control-Allow-Origin": "*", # Adjust as needed for security
27
+ "Access-Control-Allow-Methods": "POST, GET, OPTIONS, PUT, DELETE",
28
+ "Access-Control-Allow-Headers": "*",
29
  })
30
 
31
+
32
+ # Box and Truck classes
33
  class Box:
34
  def __init__(self, length, width, height, quantity, box_type):
35
  self.length = length
36
  self.width = width
37
  self.height = height
38
+ self.quantity = quantity if quantity > 0 else 1
39
  self.type = box_type
40
 
41
  def rotated_dimensions(self):
 
42
  possible_rotations = [
43
  (self.length, self.width, self.height),
44
  (self.length, self.height, self.width),
 
47
  (self.height, self.length, self.width),
48
  (self.height, self.width, self.length)
49
  ]
 
50
  return sorted(possible_rotations, key=lambda x: (x[0] * x[1], x[2]), reverse=True)
51
 
52
  def volume(self):
53
  return self.length * self.width * self.height
54
 
55
+
56
  class Truck:
57
  def __init__(self, length, width, height):
58
  self.length = length
 
60
  self.height = height
61
  self.volume = length * width * height
62
  self.placed_boxes = []
63
+ self.available_space = [(0, 0, 0, length, width, height)]
64
+
65
+ def is_space_supported(self, position, box):
66
+ x, y, z = position
67
+ bl, bw, bh = box
68
+
69
+ if z == 0:
70
+ return True
71
+
72
+ for placed_box, placed_position in self.placed_boxes:
73
+ px, py, pz = placed_position
74
+ pl, pw, ph = placed_box
75
 
76
+ if (x >= px and x + bl <= px + pl) and (y >= py and y + bw <= py + pw) and (z == pz + ph):
77
+ return True
78
+
79
+ return False
80
+
81
+ def add_box_ffd(self, box):
82
  best_fit = None
83
  best_fit_space_index = -1
84
+ best_fit_volume_waste = float('inf')
85
 
86
  for rotation in box.rotated_dimensions():
87
  for i, space in enumerate(self.available_space):
88
  x, y, z, l, w, h = space
89
 
 
90
  if rotation[0] <= l and rotation[1] <= w and rotation[2] <= h:
91
+ if not self.is_space_supported((x, y, z), rotation):
92
+ continue
93
+
94
+ leftover_volume = (l * w * h) - (rotation[0] * rotation[1] * rotation[2])
95
+
96
+ if leftover_volume < best_fit_volume_waste:
97
  best_fit = rotation
98
  best_fit_space_index = i
99
+ best_fit_volume_waste = leftover_volume
100
 
101
  if best_fit:
102
  x, y, z, l, w, h = self.available_space[best_fit_space_index]
103
  box_position = (x, y, z)
104
 
 
105
  self.placed_boxes.append((best_fit, box_position))
106
 
 
107
  self.available_space.pop(best_fit_space_index)
108
  self.update_space(best_fit, box_position, l, w, h)
109
  return box_position
 
114
  x, y, z = position
115
  bl, bw, bh = box
116
 
 
117
  new_spaces = [
118
+ (x + bl, y, z, l - bl, w, h),
119
+ (x, y + bw, z, bl, w - bw, h),
120
+ (x, y, z + bh, bl, bw, h - bh),
121
  ]
122
 
 
123
  new_spaces = [space for space in new_spaces if space[3] > 0 and space[4] > 0 and space[5] > 0]
124
 
 
125
  self.available_space.extend(new_spaces)
 
 
126
  self.available_space.sort(key=lambda space: (space[2], space[1], space[0]))
127
 
128
+
129
+ # Helper functions for box packing
130
+ def pack_boxes_ffd(truck, consignments):
131
  packed_positions = []
132
 
133
+ consignments = sorted(consignments, key=lambda c: (not c[1], -sum(box.volume() * box.quantity for box in c[0])))
 
134
 
135
+ for consignment in consignments:
136
+ consignment_packed = []
137
+ for box in consignment[0]:
138
+ for _ in range(box.quantity):
139
+ position = truck.add_box_ffd(box)
140
+ if position is None:
141
+ break
142
+ consignment_packed.append((box, position))
143
+
144
+ if len(consignment_packed) == sum(box.quantity for box in consignment[0]):
145
+ packed_positions.extend(consignment_packed)
146
+ else:
147
+ break
148
 
149
  return packed_positions
150
 
151
+
152
+ def find_max_consignments_combinations(truck, consignments, combination_limit=100):
153
+ combinations_that_fit = []
154
+ consignments_sorted = sorted(consignments, key=lambda c: (not c[1], -sum(box.volume() * box.quantity for box in c[0])))
155
+
156
+ combo_count = 0
157
+ max_consignments_fitted = 0
158
+
159
+ for r in range(1, len(consignments_sorted) + 1):
160
+ for combo in combinations(consignments_sorted, r):
161
+ combo_count += 1
162
+ print(f"Checking combination {combo_count}: {[f'Consignment {boxes[0].type}' for boxes, _ in combo]}")
163
+
164
+ if combo_count > combination_limit:
165
+ print(f"Reached combination limit of {combination_limit}. Stopping further checks.")
166
+ break
167
+
168
+ temp_truck = Truck(truck.length, truck.width, truck.height)
169
+
170
+ total_volume = sum(sum(box.volume() * box.quantity for box in consignment[0]) for consignment in combo)
171
+
172
+ if total_volume > temp_truck.volume:
173
+ continue
174
+
175
+ try:
176
+ packed_positions = pack_boxes_ffd(temp_truck, combo)
177
+ if packed_positions:
178
+ print(f"Combination {combo_count} fits with {len(combo)} consignments")
179
+ combinations_that_fit.append(combo)
180
+ max_consignments_fitted = max(max_consignments_fitted, len(combo))
181
+
182
+ if len(combinations_that_fit) >= 5:
183
+ break
184
+ except HTTPException:
185
+ print(f"Combination {combo_count} does not fit")
186
+ continue
187
+
188
+ if combo_count > combination_limit:
189
+ break
190
+
191
+ return combinations_that_fit[-5:]
192
+
193
+
194
+ def suggest_truck(consignments):
195
  trucks = {
196
  "TATA ACE": {"length": 7, "width": 4.8, "height": 4.8},
197
  "ASHOK LEYLAND DOST": {"length": 7, "width": 4.8, "height": 4.8},
 
220
  "MCV": {"length": 19, "width": 7, "height": 7}
221
  }
222
 
 
223
  sorted_trucks = sorted(trucks.items(), key=lambda t: t[1]['length'] * t[1]['width'] * t[1]['height'])
224
+
 
225
  for truck_name, dimensions in sorted_trucks:
226
+ truck = Truck(dimensions['length'] * 12, dimensions['width'] * 12, dimensions['height'] * 12)
227
+ packed_positions = pack_boxes_ffd(truck, consignments)
228
+
229
+ total_boxes = sum(box.quantity for consignment in consignments for box in consignment[0])
230
+
231
+ if len(packed_positions) == total_boxes:
232
  return {"name": truck_name, "dimensions": dimensions}
233
+
234
  return None
235
 
236
+
237
+ # Models
238
+ class BoxData(BaseModel):
239
+ PieceLength: float
240
+ PieceBreadth: float
241
+ PieceHeight: float
242
+ Priority: int
243
+
244
+
245
+ class ConsignmentData(BaseModel):
246
+ ConsignmentNo: str
247
+ Priority: int
248
+ boxes: List[BoxData]
249
+
250
+
251
+ class SubmitDataRequest(BaseModel):
252
+ truck_name: str
253
+ autoSuggest: bool
254
+ consignments_data: List[ConsignmentData]
255
+
256
+ from fastapi.responses import RedirectResponse
257
+
258
+ @app.post("/submit_data/")
259
+ async def submit_data(request: SubmitDataRequest):
260
+ """
261
+ Endpoint to receive data from the client via API.
262
+ """
263
+ truck_name = request.truck_name
264
+ autoSuggest = request.autoSuggest
265
+ consignments_data = request.consignments_data # This is already parsed from JSON
266
+
267
+ global stored_combinations, processed_data_store
268
+
269
+ # Parse consignments and calculate total quantity based on box dimensions
270
+ consignments = []
271
+ for consignment_data in consignments_data:
272
+ consignment_no = consignment_data.ConsignmentNo # ConsignmentNo is now accessible as a string
273
+ is_priority = consignment_data.Priority
274
+
275
+ # Aggregate the boxes with same dimensions
276
+ box_aggregation = {}
277
+ for box_data in consignment_data.boxes:
278
+ dimensions = (box_data.PieceLength, box_data.PieceBreadth, box_data.PieceHeight)
279
+
280
+ if dimensions in box_aggregation:
281
+ box_aggregation[dimensions]['Quantity'] += 1 # Increment the count if dimensions are the same
282
+ else:
283
+ box_aggregation[dimensions] = {
284
+ 'PieceLength': box_data.PieceLength,
285
+ 'PieceBreadth': box_data.PieceBreadth,
286
+ 'PieceHeight': box_data.PieceHeight,
287
+ 'Quantity': 1 # Initialize count to 1
288
+ }
289
+
290
+ # Convert aggregated boxes back to the expected list format
291
+ consignment_boxes = [
292
+ Box(
293
+ box['PieceLength'] / 2.54, # Convert cm to inches
294
+ box['PieceBreadth'] / 2.54,
295
+ box['PieceHeight'] / 2.54,
296
+ box['Quantity'],
297
+ f"Consignment {consignment_no} ({'Priority' if is_priority else 'Non-Priority'})"
298
+ )
299
+ for box in box_aggregation.values()
300
+ ]
301
+ consignments.append((consignment_boxes, is_priority))
302
+
303
+ # Handle autoSuggest logic and other functionality here
304
  if autoSuggest:
305
+ suggested_truck = suggest_truck(consignments)
 
306
  if suggested_truck:
307
+ truck_name = suggested_truck["name"]
308
  length = suggested_truck['dimensions']['length']
309
  width = suggested_truck['dimensions']['width']
310
  height = suggested_truck['dimensions']['height']
311
  else:
312
  raise HTTPException(status_code=400, detail="No suitable truck found")
313
+ else:
314
+ # Assuming truck dimensions are known based on truck_name
315
+ trucks = {
316
+ "TATA ACE": {"length": 7, "width": 4.8, "height": 4.8},
317
+ "ASHOK LEYLAND DOST": {"length": 7, "width": 4.8, "height": 4.8},
318
+ "MAHINDRA BOLERO PICK UP": {"length": 8, "width": 5, "height": 4.8},
319
+ "ASHOK LEYLAND BADA DOST": {"length": 9.5, "width": 5.5, "height": 5},
320
+ "TATA 407": {"length": 9, "width": 5.5, "height": 5},
321
+ "EICHER 14 FEET": {"length": 14, "width": 6, "height": 6.5},
322
+ "EICHER 17 FEET": {"length": 17, "width": 6, "height": 7},
323
+ "EICHER 19 FEET": {"length": 19, "width": 7, "height": 7},
324
+ "TATA 22 FEET": {"length": 22, "width": 7.5, "height": 7},
325
+ "TATA TRUCK (6 TYRE)": {"length": 17.5, "width": 7, "height": 7},
326
+ "TAURUS 16 T (10 TYRE)": {"length": 21, "width": 7.2, "height": 7},
327
+ "TAURUS 21 T (12 TYRE)": {"length": 24, "width": 7.3, "height": 7},
328
+ "TAURUS 25 T (14 TYRE)": {"length": 28, "width": 7.8, "height": 7},
329
+ "CONTAINER 20 FT": {"length": 20, "width": 8, "height": 8},
330
+ "CONTAINER 32 FT SXL": {"length": 32, "width": 8, "height": 8},
331
+ "CONTAINER 32 FT MXL": {"length": 32, "width": 8, "height": 8},
332
+ "CONTAINER 32 FT SXL / MXL HQ": {"length": 32, "width": 8, "height": 10},
333
+ "20 FEET OPEN ALL SIDE (ODC)": {"length": 20, "width": 8, "height": 8},
334
+ "28-32 FEET OPEN-TRAILOR JCB ODC": {"length": 28, "width": 8, "height": 8},
335
+ "32 FEET OPEN-TRAILOR ODC": {"length": 32, "width": 8, "height": 8},
336
+ "40 FEET OPEN-TRAILOR ODC": {"length": 40, "width": 8, "height": 8},
337
+ "SCV": {"length": 5, "width": 12, "height": 6},
338
+ "LCV": {"length": 11, "width": 5, "height": 5},
339
+ "ICV": {"length": 16, "width": 6.5, "height": 6.5},
340
+ "MCV": {"length": 19, "width": 7, "height": 7}
341
+ }
342
+
343
+ if truck_name not in trucks:
344
+ raise HTTPException(status_code=400, detail="Invalid truck name provided")
345
+
346
+ truck_dimensions = trucks[truck_name]
347
+ length = truck_dimensions['length']
348
+ width = truck_dimensions['width']
349
+ height = truck_dimensions['height']
350
 
351
+ # Initialize the Truck object and pack boxes
352
+ truck = Truck(length * 12, width * 12, height * 12) # Convert feet to inches
353
+ packed_positions = pack_boxes_ffd(truck, consignments)
354
+
355
+ total_boxes = sum(box.quantity for consignment in consignments for box in consignment[0])
356
+
357
+ if len(packed_positions) < total_boxes:
358
+ stored_combinations = find_max_consignments_combinations(truck, consignments)
359
+ if stored_combinations:
360
+ combinations_info = []
361
+ for index, combo in enumerate(stored_combinations):
362
+ total_quantity = sum(box.quantity for boxes, _ in combo for box in boxes)
363
+ combination_info = {
364
+ "combination_number": index + 1,
365
+ "consignments": [
366
+ {
367
+ "consignment_number": f"{boxes[0].type.split(' ')[1]}",
368
+ "priority_status": boxes[0].type.split('(')[1].strip(')')
369
+ }
370
+ for boxes, _ in combo
371
+ ],
372
+ "total_quantity": total_quantity # Add total quantity for this combination
373
+ }
374
+ combinations_info.append(combination_info)
375
+
376
+ # Generate a unique session_id
377
+ session_id = str(uuid.uuid4())
378
+ processed_data_store[session_id] = {
379
+ "error": "Not all consignments fit.",
380
+ "combinations_that_fit": combinations_info,
381
+ "truck": {
382
+ "name": truck_name,
383
+ "dimensions": {
384
+ "length": length,
385
+ "width": width,
386
+ "height": height
387
+ }
388
+ }
389
+ }
390
 
391
+ # Redirect to the visualization page
392
+ return RedirectResponse(url=f"https://66e6a59e6eaa79674a3c7c47--stellar-jelly-eac527.netlify.app?session_id={session_id}", status_code=302)
393
+
394
+
395
+ # Prepare box data for visualization
396
  box_data = [
397
  {
398
  'length': box.length,
 
405
  for box, pos in packed_positions
406
  ]
407
 
408
+ # Generate a unique session_id
409
+ session_id = str(uuid.uuid4())
410
+ processed_data_store[session_id] = {
411
  "boxes": box_data,
412
  "truck": {
413
+ "name": truck_name,
414
  "dimensions": {
415
  "length": length,
416
  "width": width,
 
418
  }
419
  }
420
  }
421
+
422
+ # Redirect to the visualization page
423
+ return RedirectResponse(url=f"https://66e6a59e6eaa79674a3c7c47--stellar-jelly-eac527.netlify.app?session_id={session_id}", status_code=302)
424
+
425
+
426
+
427
+ @app.post("/load_combination/")
428
+ async def load_combination(
429
+ index: int = Form(...),
430
+ truck_length: float = Form(...),
431
+ truck_width: float = Form(...),
432
+ truck_height: float = Form(...),
433
+ truck_name: str = Form(...)
434
+ ):
435
+ """
436
+ Endpoint to visualize the selected combination.
437
+ """
438
+ print(f"Received index: {index}")
439
+ print(f"Received truck details: {truck_length}L x {truck_width}W x {truck_height}H, Name: {truck_name}")
440
+
441
+ # Ensure the index is within the valid range
442
+ if index >= len(stored_combinations):
443
+ raise HTTPException(status_code=400, detail="Invalid combination index")
444
+
445
+ selected_combination = stored_combinations[index]
446
+
447
+ # Create a Truck object using the received truck dimensions
448
+ truck = Truck(truck_length * 12, truck_width * 12, truck_height * 12) # Convert feet to inches
449
+
450
+ # Unpack and structure the combination data for the pack_boxes_ffd function
451
+ consignments = [
452
+ (boxes, priority) for boxes, priority in selected_combination # Unpack the combination as expected
453
+ ]
454
+
455
+ # Generate the box data for this combination
456
+ packed_positions = pack_boxes_ffd(truck, consignments)
457
+
458
+ # Prepare the box data
459
+ box_data = [
460
+ {
461
+ 'length': box.length,
462
+ 'width': box.width,
463
+ 'height': box.height,
464
+ 'type': box.type,
465
+ 'quantity': box.quantity,
466
+ 'position': {'x': pos[0], 'y': pos[1], 'z': pos[2]}
467
+ }
468
+ for box, pos in packed_positions
469
+ ]
470
+
471
+ # Return the box data and truck information to the frontend
472
+ return {
473
+ "boxes": box_data,
474
+ "truck": {
475
+ "name": truck_name,
476
+ "dimensions": {
477
+ "length": truck_length,
478
+ "width": truck_width,
479
+ "height": truck_height
480
+ }
481
+ }
482
+ }
483
+
484
+
485
+ @app.get("/get_visualization/{session_id}/")
486
+ async def get_visualization(session_id: str):
487
+ """
488
+ Endpoint for the frontend to fetch processed data using session_id.
489
+ """
490
+ if session_id not in processed_data_store:
491
+ raise HTTPException(status_code=404, detail="Session ID not found")
492
+
493
+ return processed_data_store[session_id]