def minimize(args): """Minimize the cost of a purchase""" ################# Loading ################################## # load in wanted parts lists if args.parts_list.endswith(".bsx"): wanted_parts = io.load_bsx(open(args.parts_list)) else: wanted_parts = io.load_xml(open(args.parts_list)) print 'Loaded %d different parts' % len(wanted_parts) # load in pricing data available_parts = io.load_price_guide(open(args.price_guide)) n_available = len(available_parts) n_stores = len(set(e['store_id'] for e in available_parts)) print 'Loaded %d available lots from %d stores' % (n_available, n_stores) # load in store metadata if args.store_list is not None: store_metadata = io.load_store_metadata(open(args.store_list)) print 'Loaded metadata for %d stores' % len(store_metadata) ################# Filtering Stores ######################### # select which stores to get parts from allowed_stores = list(store_metadata) if args.source_country is not None: print 'Only allowing stores from %s' % (args.source_country,) allowed_stores = filter(lambda x: x['country_name'] == args.source_country, allowed_stores) if args.target_country is not None: print 'Only allowing stores that ship to %s' % (args.target_country,) allowed_stores = [s for s in allowed_stores if args.target_country in s['ships'] or (len(s['ships']) == 1 and s['ships'][0] == 'All Countries WorldWide')] if args.feedback is not None and args.feedback > 0: print 'Only allowing stores with feedback >= %d' % (args.feedback,) allowed_stores = filter(lambda x: x['feedback'] >= args.feedback, allowed_stores) if args.exclude is not None: excludes = set(args.exclude.strip().split(",")) excludes = map(lambda x: int(x), excludes) print 'Forcing exclusion of: %s' % (excludes,) allowed_stores = filter(lambda x: not (x['store_id'] in excludes), allowed_stores) store_ids = map(lambda x: x['store_id'], allowed_stores) store_ids = list(set(store_ids)) print 'Using %d stores' % len(store_ids) available_parts = filter(lambda x: x['store_id'] in store_ids, available_parts) solution = minimizer.greedy(wanted_parts, available_parts)[0] if not minimizer.is_valid_solution(wanted_parts, solution['allocation']): print ("You're too restrictive. There's no way to buy what " + "you want with these stores") sys.exit(1) ################# Minimization ############################# if args.algorithm in ['ilp', 'greedy']: if args.algorithm == 'ilp': ### Integer Linear Programming ### solution = minimizer.gurobi( wanted_parts, available_parts, allowed_stores, shipping_cost=args.shipping_cost )[0] assert minimizer.is_valid_solution(wanted_parts, solution['allocation'], allowed_stores) elif args.algorithm == 'greedy': ### Greedy Set Cover ### solution = minimizer.greedy(wanted_parts, available_parts)[0] # check and save io.save_solution(open(args.output + ".json", 'w'), solution) # print outs stores = set(e['store_id'] for e in solution['allocation']) cost = solution['cost'] unsatisified = minimizer.unsatisified(wanted_parts, solution['allocation']) print 'Total cost: $%.2f | n_stores: %d | remaining lots: %d' % (cost, len(stores), len(unsatisified)) elif args.algorithm == 'brute-force': # for each possible number of stores for k in range(1, args.max_n_stores): # find all possible solutions using k stores solutions = minimizer.brute_force(wanted_parts, available_parts, k) solutions = list(sorted(solutions, key=lambda x: x['cost'])) solutions = solutions[0:10] # save output output_folder = os.path.join(args.output, str(k)) try: os.makedirs(output_folder) except OSError: pass for (i, solution) in enumerate(solutions): output_path = os.path.join(output_folder, "%02d.json" % i) with open(output_path, 'w') as f: io.save_solution(f, solution) # print outs if len(solutions) > 0: print '%8s %40s' % ('Cost', 'Store IDs') for sol in solutions: print '$%7.2f %40s' % (sol['cost'], ",".join(str(s) for s in sol['store_ids'])) else: print "No solutions using %d stores" % k
def wanted_list(args): """Create BrickLink Wanted Lists for each store""" # load recommendation recommendation = io.load_solution(open(args.recommendation)) store_metadata = io.load_store_metadata(open(args.store_list)) io.save_xml_per_vendor(args.output, recommendation, store_metadata)