def test_pack_boxes_tight_fit_many(self): ''' tests a tight fit for non-cubic items ''' item = ItemTuple('Item1', [1, 3, 3], 0) item_info = [item] * 82 box_dims = [9, 9, 9] packed_items = pack_boxes(box_dims, item_info) expected_return = [[item] * 81, [item]] self.assertEqual(2, len(packed_items)) self.assertEqual(expected_return, packed_items)
def test_pack_boxes_100_items(self): ''' test many items into one box with inexact fit ''' item = ItemTuple('Item1', [5, 5, 5], 0) item_info = [item] * 100 box_dims = [51, 51, 6] packed_items = pack_boxes(box_dims, item_info) self.assertEqual(len(packed_items), 1)
def test_pack_boxes_three_item_one_box(self): ''' test odd sized items will be rotated to fit ''' item1 = ItemTuple('Item1', [13, 13, 31], 0) item2 = ItemTuple('Item2', [8, 13, 31], 0) item3 = ItemTuple('Item3', [5, 13, 31], 0) item_info = [item1, item2, item3] box_dims = [13, 26, 31] packed_items = pack_boxes(box_dims, item_info) self.assertEqual(packed_items, ([[item1, item2, item3]]))
def test_pack_boxes_tight_fit_many_oblong_inexact(self): ''' tests that the algorithm remains at least as accurate as it already is if it were perfect, the first box would have 48 in it ''' item = ItemTuple('Item1', [1, 2, 3], 0) item_info = [item] * 49 box_dims = [4, 8, 9] packed_items = pack_boxes(box_dims, item_info) self.assertEqual(2, len(packed_items)) self.assertGreaterEqual(44, len(packed_items[0]))
def test_pack_boxes_dim_over_2(self): ''' test that when length of item <= length of box / 2 it packs along longer edge ''' item = ItemTuple('Item1', [3, 4, 5], 0) item_info = [item] * 4 box_dims = [6, 8, 10] packed_items = pack_boxes(box_dims, item_info) self.assertEqual(packed_items, ([[item, item, item, item]]))
def test_pack_3_boxes(self): ''' test that multiple parcels will be selected ''' item = ItemTuple('Item1', [4, 4, 12], 0) item_info = [item] * 3 # item_info = [['Item1', [2, 2, 12]]] * 3 # no error # item_info = [['Item1', [4, 4, 12]]] * 2 # no error box_dims = [4, 4, 12] packed_items = pack_boxes(box_dims, item_info) self.assertEqual(packed_items, ([[item], [item], [item]]))
def test_slightly_larger_box(self): ''' test inexact dimensions ''' # Fails due to recursion limits reached item = ItemTuple('Item1', [4, 4, 12], 0) item_info = [item] * 2 box_dims = [5, 8, 12] # box_dims = [4, 8, 12] # passes packed_items = pack_boxes(box_dims, item_info) self.assertEqual(packed_items, ([[item, item]]))
def test_pack_boxes_odd_sizes(self): ''' test odd sized items will be rotated to fit ''' item1 = ItemTuple('Item1', [3, 8, 10], 0) item2 = ItemTuple('Item2', [1, 2, 5], 0) item3 = ItemTuple('Item3', [1, 2, 2], 0) item_info = [item1, item2, item2, item3] box_dims = [10, 20, 20] packed_items = pack_boxes(box_dims, item_info) self.assertEqual([[item1, item2, item2, item3]], packed_items)
def test_pack_boxes_100_items_2_boxes(self): ''' test many items separated into 2 boxes with exact fit ''' item = ItemTuple('Item1', [5, 5, 5], 0) item_info = [item] * 100 box_dims = [10, 25, 25] packed_items = pack_boxes(box_dims, item_info) self.assertEqual(len(packed_items), 2) self.assertEqual(len(packed_items[0]), 50) self.assertEqual(len(packed_items[1]), 50)
def test_pack_boxes_odd_sizes_again(self): ''' test items with different dimensions will be rotated to fit into one box ''' item1 = ItemTuple('Item1', [1, 18, 19], 0) item2 = ItemTuple('Item2', [17, 18, 18], 0) item3 = ItemTuple('Item3', [1, 17, 18], 0) item_info = [item1, item2, item3] box_dims = [18, 18, 19] packed_items = pack_boxes(box_dims, item_info) self.assertEqual(packed_items, ([[item1, item2, item3]]))
def compare_pyshipping_with_shotput(): from random import randint from pyshipping import binpack_simple as binpack from pyshipping.package import Package from time import time items = [] py_items = [] box_dims = sorted( [randint(100, 200), randint(100, 200), randint(100, 200)]) num_items = 500 for _ in xrange(num_items): item_dims = sorted( [randint(20, 100), randint(20, 100), randint(20, 100)]) items.append(ItemTuple(str(volume(item_dims)), item_dims, 0)) py_items.append(Package((item_dims[0], item_dims[1], item_dims[2]), 0)) start = time() items_packed = pack_boxes(box_dims, items) end = time() shotput = { 'num_parcels': len(items_packed), 'items_per_parcel': [len(parcel) for parcel in items_packed], 'time': end - start } py_box = Package((box_dims[0], box_dims[1], box_dims[2]), 0) start = time() py_items_packed = binpack.packit(py_box, py_items) end = time() pyshipping = { 'num_parcels': len(py_items_packed[0]), 'items_per_parcel': [len(parcel) for parcel in py_items_packed[0]], 'time': end - start } if len(items_packed) > len(py_items_packed[0]): best_results = 'pyshipping' elif len(items_packed) < len(py_items_packed[0]): best_results = 'shotput' else: best_results = 'tie' return { 'shotput': shotput, 'pyshipping': pyshipping, 'best_results': best_results }
def pre_pack_boxes(box_info, items_info, options): ''' returns the packed items of one specific box based on item_info the item info input does not require a db call Args boxes_info (Dict[ weight: float height: float length: float width: float dimension_units: ('inches', 'centimeters', 'feet', 'meters') weight_units: ('grams', 'pounds', 'kilograms', 'onces') name: String ]) products_info (List[Dict[ weight: float height: float length: float width: float dimension_units: ('inches', 'centimeters', 'feet', 'meters') weight_units: ('grams', 'pounds', 'kilograms', 'onces') product_name: String ]) options (Dict[ max_weight: float ]) Returns List[Dict[{ packed_products: Dict[item, qty], total_weight: float }]] ''' dimension_units = box_info['dimension_units'] box_dims = sorted( [box_info['width'], box_info['length'], box_info['height']]) items_to_pack = [] weight_units = box_info['weight_units'] box_weight = box_info['weight'] total_weight = box_weight max_weight = options.get('max_weight', 31710) # given max weight or 70lbs for item in items_info: dimension_units = item['dimension_units'] weight_units = item['weight_units'] sorted_dims = sorted([item['height'], item['length'], item['width']]) if not does_it_fit(sorted_dims, box_dims): raise BoxError('Some of your items are too big for the box you\'ve' ' selected. Please select a bigger box or contact' ' [email protected].') item['weight'] = item['weight'] items_to_pack += [ ItemTuple(item['product_name'], sorted_dims, int(item['weight'])) ] * int(item['quantity']) total_weight += item['weight'] * int(item['quantity']) items_to_pack = sorted(items_to_pack, key=lambda item: item.dimensions[2], reverse=True) box_dims = sorted(box_dims) items_packed = pack_boxes(box_dims, items_to_pack) if math.ceil(float(total_weight) / max_weight) > len(items_packed): additional_box = [] for items in items_packed: while weight_of_box_contents(items) + box_weight > max_weight: if (weight_of_box_contents(additional_box) + items[-1].weight <= max_weight): additional_box.append(items.pop()) else: items_packed.append(list(additional_box)) additional_box = [items.pop()] items_packed.append(additional_box) parcel_shipments = [] for items in items_packed: item_qty = Counter() parcel_weight = box_weight for item in items: item_qty[item.item_number] += 1 parcel_weight += item.weight parcel_shipments.append({ 'packed_products': dict(item_qty), 'total_weight': parcel_weight }) return parcel_shipments