def get_person(url): person = None try: person = db.open().query(Person).filter(Person.api_url == url).one() except: person = Person() json_data = get_json(url) person.parse_dictionary(json_data) db.save(person) for starship_url in json_data['starships']: starship = get_starship(starship_url) pilot = Pilot() pilot.person = person pilot.starship = starship db.save(pilot) for vehicle_url in json_data['vehicles']: vehicle = get_vehicle(vehicle_url) driver = Driver() driver.person = person driver.vehicle = vehicle db.save(driver) return person
def get_person(url): if (url is None or len(url) < 1): return None person = None try: person = db.open().query(Person).filter(Person.api_url == url).one() except NoResultFound: pass except Exception as exp: print(exp) pass if (person is None): json_dict = get_json_dict(url) if (json_dict is not None and 'url' in json_dict): person = Person() person.parse_dict(json_dict) try: db.save(person) except Exception as exp: print(exp) planet = get_planet(json_dict['homeworld']) if (planet is not None): person.planet = planet try: db.save(person) except Exception as exp: print(exp) for starship_url in json_dict['starships']: starship = get_starship(starship_url) persist_pilot(person, starship) for vehicle_url in json_dict['vehicles']: vehicle = get_vehicle(vehicle_url) persist_driver(person, vehicle) return person
def get_person(url): person = None try: person = db.open().query(Person).filter(Person.api == url).one() except: person = Person() json_data = get_json(url) person.parse_json(json_data) db.save(person) current_job_url = json_data['current_job'] if current_job_url: department = get_department(current_job_url) current_job = get_job(person, department, 1) for past_job_url in json_data['employment_history']: department = get_department(past_job_url) past_job = get_job(person, department, 0) current_address = json_data['current_address'] if current_address: city = get_city_from_address(current_address) address = get_address(person, city, 1) for past_address in json_data['past_addresses']: city = get_city_from_address(past_address) address = get_address(person, city, 0) return person
def generate(self, round_num: int) -> Dict[int, List[Person]]: """ Returns a Dictionary that maps the arriving people to the floor which they arrive at during a certain round (: round_num: The round which the arrivals are to take place """ if self.num_people is None: arrival_amount = random.randint(0, 10) else: arrival_amount = self.num_people mapper = {} persons = [] q = self.max_floor for i in range(q): mapper[i + 1] = [] for i in range(arrival_amount + 1): r_start = random.randint(1, q) r_end = random.randint(1, q) if r_start == r_end: while r_start == r_end: r_end = random.randint(0, q) else: for k in mapper: if k == r_start: mapper[k].append(Person(r_start, r_end)) persons.append(Person(r_start, r_end)) for g in range(q): for z in range(len(persons)): if persons[z] == z: mapper[z].append(persons[z]) return mapper
def generate(self, round_num: int) -> Dict[int, List[Person]]: """Refer to the Parent class """ for num in range(1, self.num_floor + 1): self.people[num] = [] person_location = [] person_destination = [] if self.num_people == 0: return self.people else: i = 0 while i < self.num_people: floors = list(range(1, self.max_floor + 1)) location = random.sample(floors, 1) person_location += location floors.pop(floors.index(location[0])) destination = random.sample(floors, 1) person_destination += destination i += 1 while len(person_location) > 0: cur_loc = person_location[0] cur_dest = person_destination[0] self.people[cur_loc] += [Person(cur_loc, cur_dest)] person_destination.pop(0) person_location.pop(0) return self.people
def generate(self, round_num: int) -> Dict[int, List[Person]]: """ Generate a fixed number of new arrivals with random start and target floors at the given round. Return the dictionary mapping floor numbers to the people who arrived starting at that floor. """ wait_list = {} i = 1 while i <= self.num_people: a = random.sample(range(1, self.max_floor + 1), 2) if a[0] not in wait_list: wait_list[a[0]] = [Person(a[0], a[1])] else: wait_list[a[0]].append(Person(a[0], a[1])) i += 1 return wait_list
def __init__(self, max_floor: int, filename: str) -> None: """Initialize a new FileArrivals algorithm from the given file. The num_people attribute of every FileArrivals instance is set to None, since the number of arrivals depends on the given file. Precondition: <filename> refers to a valid CSV file, following the specified format and restrictions from the assignment handout. """ ArrivalGenerator.__init__(self, max_floor, None) self.arrivals = {} # We've provided some of the "reading from csv files" boilerplate code # for you to help you get started. with open(filename) as csvfile: reader = csv.reader(csvfile) for line in reader: ln = int(line[0]) self.arrivals[ln] = [] #instantiate all people for the simulation for i in range(1, len(line) - 1, 2): new_person = Person(int(line[i]), int(line[i + 1])) self.arrivals[ln].append(new_person)
def __init__(self, max_floor: int, filename: str) -> None: """Initialize a new FileArrivals algorithm from the given file. The num_people attribute of every FileArrivals instance is set to None, since the number of arrivals depends on the given file. Precondition: <filename> refers to a valid CSV file, following the specified format and restrictions from the assignment handout. """ ArrivalGenerator.__init__(self, max_floor, None) # We've provided some of the "reading from csv files" boilerplate code # for you to help you get started. self._arrivals = {} with open(filename) as csvfile: reader = csv.reader(csvfile) for line in reader: # convert strings to ints # set var round_num round_num = int(line.pop(0)) self._arrivals[round_num] = {} while len(line) > 0: person = Person(int(line.pop(0)), int(line.pop(0))) if person.start not in self._arrivals[round_num]: self._arrivals[round_num][person.start] = [] self._arrivals[round_num][person.start].append(person)
def generate(self, round_num: int) -> Optional[Dict[int, List[Person]]]: """Randomly generates num_people for the given round_num Returns a list containing the floors in the simulation and the people (if any) that have just arrived on that floor """ #If the number of people to generate is not None if self.num_people is not None: arrivals = {} #Add a key to arrivals for every floor starting at 1 to max_floor for i in range(1, self.max_floor + 1): neww = {i: []} arrivals.update(neww) pop = [] #populate pop with all possible floor numbers for j in range(1, self.max_floor + 1): pop.append(j) #generate num_people people for i in range(0, self.num_people): neww = random.sample(pop, 2) start = neww[0] target = neww[1] new = Person(start, target) arrivals[start].append(new) return arrivals else: return None
def generate(self, round_num: int) -> Dict[int, List[Person]]: """Return the new arrivals for the simulation at the given round. The returned dictionary maps floor number to the people who arrived starting at that floor. You can choose whether to include floors where no people arrived. """ arriving_people = dict() arriving_people[round_num] = [] # Creates a list 2 * the length of generations with each pair # representing a person with start and end values if self.num_people is not None: people_info = random.sample(range(1, self.max_floor), self.num_people * 2) i = 0 while i < len(people_info): arriving_people[round_num].append( Person(people_info[i], people_info[i + 1])) i += 2 return arriving_people else: return arriving_people
def __init__(self, max_floor: int, filename: str) -> None: """Initialize a new FileArrivals algorithm from the given file. The num_people attribute of every FileArrivals instance is set to None, since the number of arrivals depends on the given file. Precondition: <filename> refers to a valid CSV file, following the specified format and restrictions from the assignment handout. """ ArrivalGenerator.__init__(self, max_floor, None) self.generator = {} with open(filename) as csvfile: reader = csv.reader(csvfile) for line in reader: temp = {} i = 1 while i + 1 < len(line): people = Person(int(line[i]), int(line[i + 1]), 0) if int(line[i].strip()) not in temp: temp[int(line[i].strip())] = [people] else: temp[int(line[i].strip())].extend([people]) self.generator[int(line[0].strip())] = temp i += 2
def generate(self, round_num: int) -> Dict[int, List[Person]]: """Return the new arrivals for the simulation at the given round. The starting floor and target floor of the new arrivals are random. The returned dictionary maps floor number to the people who arrived starting at that floor. """ list_of_people = [] floor_to_people = {} for i in range(1, self.max_floor + 1): floor_to_people[i] = [] if self.num_people: for i in range(self.num_people): start_floor = random.randint(1, self.max_floor) target_floor = random.randint(1, self.max_floor) while start_floor == target_floor: target_floor = random.randint(1, self.max_floor) new_person = Person(start_floor, target_floor) list_of_people.append(new_person) for person in list_of_people: floor_to_people[person.start].append(person) return floor_to_people
def __init__(self, max_floor: int, filename: str) -> None: """Initialize a new FileArrivals algorithm from the given file. The num_people attribute of every FileArrivals instance is set to None, since the number of arrivals depends on the given file. Precondition: <filename> refers to a valid CSV file, following the specified format and restrictions from the assignment handout. """ super().__init__(max_floor, None) self.rounds = {} with open(filename) as csvfile: reader = csv.reader(csvfile) for line in reader: ints = [int(e) for e in line] round_ = ints.pop(0) if round_ not in self.rounds: self.rounds[round_] = {i + 1:[] \ for i in range(self.max_floor)} while len(ints) > 1: start = ints.pop(0) target = ints.pop(0) self.rounds[round_][start].append(Person(start, target))
def __init__(self, max_floor: int, filename: str) -> None: """Initialize a new FileArrivals algorithm from the given file. The num_people attribute of every FileArrivals instance is set to None, since the number of arrivals depends on the given file. Precondition: <filename> refers to a valid CSV file, following the specified format and restrictions from the assignment handout. """ ArrivalGenerator.__init__(self, max_floor, None) self.generate_list = {} # We've provided some of the "reading from csv files" boilerplate code # for you to help you get started. with open(filename) as csvfile: reader = csv.reader(csvfile) for line in reader: data = list(map(int, line)) round_ = data[0] self.generate_list[round_] = {} for i in range(1, self.max_floor + 1): self.generate_list[round_][i] = [] person_index = 1 while person_index < len(data): start = data[person_index] target = data[person_index + 1] self.generate_list[round_][start].append( Person(start, target)) person_index += 2
def generate_people(self, round_num: int) -> List[Person]: """Returns a list of generated people based on the CSV file format """ people = [] for i in range(0, len(self.csv[round_num]), 2): people.append( Person(self.csv[round_num][i], self.csv[round_num][i + 1])) return people
def generate(self, round_num: int) -> Dict[int, List[Person]]: """Return new arrivals for the simulation at the given round. The returned dictionary maps floor number to the people who arrived starting at that floor. You can choose whether to include floors where no people arrived. """ wait_list = {} people = self.wait_file.get(round_num, []) i = 0 while i < len(people): if people[i] not in wait_list: wait_list[people[i]] = [Person(people[i], people[i + 1])] else: wait_list[people[i]].append(Person(people[i], people[i + 1])) i = i + 2 return wait_list
def create_person(firstname, lastname, email): p_key = ndb.Key(Person, string.join((firstname, lastname, email), '|')) p = Person(key=p_key) p.first_name = firstname p.last_name = lastname p.email = email p.put() return p
def testSimulateNewDay(self): repo = FileRepo("test.txt", Person.fileRead, Person.fileWrite) srv = Service(repo) p1 = Person(1, 'nonvaccinated', 'ill') p2 = Person(2, 'nonvaccinated', 'healthy') p3 = Person(3, 'nonvaccinated', 'healthy') repo.add(p1) repo.add(p2) repo.add(p3) srv.simulateNewDay() self.assertEqual(p2.getStatus(), 'ill') srv.simulateNewDay() self.assertEqual(p3.getStatus(), 'ill') with self.assertRaises(AssertionError): assert p1.getStatus() == 'healthy' repo.clearRepo()
def generate(self, round_num: int) -> Dict[int, List[Person]]: res = {} for i in range(1, self.max_floor + 1): res[i] = [] for i in range(self.num_people): start = random.randint(1, self.max_floor) target = random.randint(1, self.max_floor) while start == target: start = random.randint(1, self.max_floor) res[start].append(Person(start, target)) return res
def generate(self, round_num: int) -> Dict[int, List[Person]]: """Return the new arrivals for the simulation at the given round. The returned dictionary maps floor number to the people who arrived starting at that floor. You can choose whether to include floors where no people arrived. """ person_dict = {} for floor in range(1, self.max_floor + 1): person_dict[floor] = [] for person in range(self.num_people): person = Person( random.sample(range(1, self.max_floor + 1), 1)[0], random.sample(range(1, self.max_floor + 1), 1)[0]) while person.start == person.target: person.target = \ random.sample(range(1, self.max_floor + 1), 1)[0] person_dict[person.start].append(person) return person_dict
def generate_people(self) -> List[Person]: """ Return a list of people with randomly generated starting and target floors """ people = [] possible_floors = list(range(1, self.max_floor + 1)) for _ in range(self.num_people): rand_floors = random.sample(possible_floors, 2) people.append(Person(rand_floors[0], rand_floors[1])) return people
def generate(self, round_num: int) -> Dict[int, List[Person]]: """Generate a Dict in which each floor index is corresponding to a list of person that arrived at this floor with the given file. """ res = {} for floor in range(1, self.max_floor + 1): res[floor] = [] round_arrival = self.arrival_dict.get(round_num, []) for people in range(int(len(round_arrival) / 2)): start = round_arrival[people * 2] target = round_arrival[people * 2 + 1] res[start] = res.get(start, []) + [Person(start, target)] return res
def generate(self, round_num: int) -> Dict[int, List[Person]]: new_arrivals = {} if self.num_people is not None: for _ in range(self.num_people): start = random.randint(1, self.max_floor) target = random.randint(1, self.max_floor) while target == start: target = random.randint(1, self.max_floor) new_person = Person(start, target) try: new_arrivals[start].append(new_person) except KeyError: new_arrivals[start] = [new_person] return new_arrivals
def generate(self, round_num: int) -> Dict[int, List[Person]]: #new_people = [] newPeople = {} for i in range(self.num_people): start = random.randint(1, self.max_floor) target = random.randint(1, self.max_floor) while target == start: start = random.randint(1, self.max_floor) target = random.randint(1, self.max_floor) person = Person(start, target, 0) if start in newPeople.keys(): newPeople[start].append(person) else: newPeople[start] = [person] return newPeople
def generate(self, round_num: int) -> Dict[int, List[Person]]: # Instantiates and populates dict with empty list for each round num # i + 1 b/c first floor is at 1 arriving_people = dict() arriving_people[round_num] = [] for line in self.file_lines: if len(line) > 0 and int(line[0]) == round_num: i = 1 while i < len(line): arriving_people[round_num].append( Person(int(line[i]), int(line[i + 1]))) i += 2 return arriving_people
def generate(self, round_num: int) -> Dict[int, List[Person]]: mapper = {} # maps floor number to person # TODO fix the implementation of generate for the csv file / # TODO try not to kill yourself when doing this. q = self.max_floor b = self.csv_line for i in range(q): mapper[i + 1] = [] for round_inst in b: for c in range(2, len(round_inst), 2): if round_inst[0] == round_num: mapper[round_inst[c - 1]].append( Person(round_inst[c - 1], round_inst[c])) return mapper
def generate(self, round_num: int) -> Dict[int, List[Person]]: """Return the new arrivals for the simulation at the given round. See parent abstract class ArrivalGenerator for details. This implementation includes floors where no people arrived. """ arrivals = {j + 1: [] for j in range(self.max_floor)} if self.num_people is None: return arrivals for _ in range(self.num_people): start = random.randint(1, self.max_floor) targets = [j + 1 for j in range(self.max_floor) if j + 1 != start] target = targets[random.randint(0, len(targets) - 1)] arrivals[start].append(Person(start, target)) return arrivals
def generate(self, round_num: int) -> Dict[int, List[Person]]: """Refer to the Parent Class """ for num in range(1, self.max_floor + 1): self.people[num] = [] file = [] for line in self.file_content: file.append(list(map(int, line))) for line in file: if line[0] == round_num: line.pop(0) for thing in line[0::2]: loc = thing dest = line[line.index(thing) + 1] self.people[thing] = [Person(loc, dest)] line.pop(0) # remove first location line.pop(0) # remove first destination elif line[0] != round_num: continue return self.people
def generate(self, round_num: int) -> Dict[int, List[Person]]: """Return the new arrivals for the simulation at the given round. The returned dictionary maps floor number to the people who arrived starting at that floor. """ list_of_people = [] floor_to_people = {} for i in range(1, self.max_floor + 1): floor_to_people[i] = [] for l in self.round_to_people.get(round_num, []): new_person = Person(l[0], l[1]) list_of_people.append(new_person) for person in list_of_people: floor_to_people[person.start].append(person) return floor_to_people
def generate(self, round_num: int) -> Dict[int, List[Person]]: """ create a dict and stores person given by pairs of random start and target """ if self.num_people is None: self.num_people = 0 generated = {} for i in range(1, self.max_floor + 1): generated[i] = [] for i in range(0, self.num_people): start = random.randint(1, self.max_floor) target = random.randint(1, self.max_floor) while target == start: target = random.randint(1, self.max_floor) generated[start].append(Person(start, target)) return generated
class Main(Scene): FADE_SPEED = 75 def load(self): self.world_size = Size(3000, 3000) self.camera = Camera(self.size, self.world_size, 1000, 10) self._tool = None self.tool = None self.batch = graphics.Batch() self.background = CameraGroup(graphics.OrderedGroup(0), self.camera) self.foreground = CameraGroup(graphics.OrderedGroup(1), self.camera) self.playerg = CameraGroup(graphics.OrderedGroup(2), self.camera) self.world_ui = CameraGroup(graphics.OrderedGroup(3), self.camera) self.ui = graphics.OrderedGroup(2) self.space = Space() self.space.gravity = (0.0, 0.0) buffer = 100 borders = Body() borders.position = (0, 0) left = Segment(borders, (-buffer, -buffer), (-buffer, self.world_size.height+buffer), buffer) bottom = Segment(borders, (-buffer, -buffer), (self.world_size.width+buffer, -buffer), buffer) right = Segment(borders, (self.world_size.width+buffer, self.world_size.height+buffer), (self.world_size.width+buffer, -buffer), buffer) top = Segment(borders, (self.world_size.width+buffer, self.world_size.height+buffer), (-buffer, self.world_size.height+buffer), buffer) self.space.add_static(left, bottom, right, top) self.stars = Stars(self.world_size, self.batch, self.background) self.asteroids = Asteroid.populate(50, 100, self.world_size, self.batch, self.foreground, self.space) if not self.asteroids: print("None of a particular resource on this asteroid belt, that'd be unfair. Trying again.") self.end(Main()) return self.home_world = choice([asteroid for asteroid in self.asteroids if asteroid.position.y > self.world_size.height/4*3]) self.home_world.type = "home" self.home_world.populated = True x, y = self.home_world.position self.camera.move(Vector(x-self.size.width/2, y-self.size.height/2)) # Let's make stuff a bit more interesting. for asteroid in self.asteroids: if not asteroid.type == "home": asteroid.body.apply_impulse((triangular(-20000, 20000, 0), triangular(-20000, 20000, 0))) x, y = self.home_world.position self.player = Person(x+150, y+150, self.batch, self.playerg, self.space) self.mouse = x+150, y+150 centre = Vector(self.size.width/2, self.size.height/2) image = centre_image(resource.image("logo.png")) self.logo = sprite.Sprite(image, centre.x, centre.y, batch=self.batch, group=self.ui) self.logo.opacity = 255 self.fade = True self.faded = False planet = centre_image(resource.image("planet.png")) x = self.world_size.width/2 y = planet.height/2 self.planet_sprite = sprite.Sprite(planet, x, y, batch=self.batch, group=self.world_ui) self.win_box = BB(x-200, y-200, x+200, y+200) #self.tools = sorted([tool(self.space) for tool in Tool.__subclasses__()], key=attrgetter("order"), reverse=True) #self.buttons = {tool: Button(30, 30+number*50, tool.image, tool.description, self.use_tool(tool), self.ui, self.batch) for number, tool in enumerate(self.tools)} self.constraints = set() def use_tool(self, tool): """For callback usage.""" def f(): self.tool = tool return f @property def tool(self): return self._tool @tool.setter def tool(self, tool): if tool: if self._tool and not self._tool == tool: self._tool.end_selecting() self.buttons[self._tool].normal() self.window.set_mouse_cursor(self.window.get_system_mouse_cursor(self.window.CURSOR_DEFAULT)) self.selecting = False self.selection = [] self.window.set_mouse_cursor(self.window.get_system_mouse_cursor(self.window.CURSOR_CROSSHAIR)) self.selecting = True self.buttons[tool].using() else: if self._tool: self._tool.end_selecting() self.buttons[self._tool].normal() self.window.set_mouse_cursor(self.window.get_system_mouse_cursor(self.window.CURSOR_DEFAULT)) self.selecting = False self.selection = [] self._tool = tool def key_pressed(self, symbol, modifiers): self.fade = True #self.camera.key_pressed(symbol) def key_released(self, symbol, modifiers): pass #self.camera.key_released(symbol) def mouse_pressed(self, x, y, key, modifiers): self.fade = True #for button in self.buttons.values(): # if button.point_over(x, y): # button.callback() # return if self.selecting: for asteroid in self.asteroids: clicked = self.camera.translate(x, y) if asteroid.point_over(*clicked): self.selection.append((asteroid, clicked)) self.tool = self.tool.selection(self.selection, self.constraints) return self.tool = None return def mouse_motion(self, x, y, dx, dy): self.mouse = self.camera.translate(x, y) #for button in self.buttons.values(): # if button.point_over(x, y): # button.on_mouse_over() # else: # button.on_mouse_leave() def mouse_drag(self, x, y, dx, dy, buttons, modifiers): if buttons & window.mouse.RIGHT: self.camera.mouse_dragged(dx, dy) def update(self, frame_time): self.constraints = {constraint for constraint in self.constraints if not constraint.update(self.batch, self.foreground)} if self.fade and not self.faded: self.logo.opacity -= Main.FADE_SPEED*frame_time if self.logo.opacity < 0: self.logo.opacity = 0 del self.logo self.faded = True self.player.target = self.mouse self.player.update() x, y = self.player.body.position self.camera.x, self.camera.y = x-self.size.width/2, y-self.size.height/2 self.camera.update(frame_time) self.space.step(1/60) if self.win_box.contains_vect(self.player.body.position): self.end(Win()) def draw(self): self.batch.draw()
def load(self): self.world_size = Size(3000, 3000) self.camera = Camera(self.size, self.world_size, 1000, 10) self._tool = None self.tool = None self.batch = graphics.Batch() self.background = CameraGroup(graphics.OrderedGroup(0), self.camera) self.foreground = CameraGroup(graphics.OrderedGroup(1), self.camera) self.playerg = CameraGroup(graphics.OrderedGroup(2), self.camera) self.world_ui = CameraGroup(graphics.OrderedGroup(3), self.camera) self.ui = graphics.OrderedGroup(2) self.space = Space() self.space.gravity = (0.0, 0.0) buffer = 100 borders = Body() borders.position = (0, 0) left = Segment(borders, (-buffer, -buffer), (-buffer, self.world_size.height+buffer), buffer) bottom = Segment(borders, (-buffer, -buffer), (self.world_size.width+buffer, -buffer), buffer) right = Segment(borders, (self.world_size.width+buffer, self.world_size.height+buffer), (self.world_size.width+buffer, -buffer), buffer) top = Segment(borders, (self.world_size.width+buffer, self.world_size.height+buffer), (-buffer, self.world_size.height+buffer), buffer) self.space.add_static(left, bottom, right, top) self.stars = Stars(self.world_size, self.batch, self.background) self.asteroids = Asteroid.populate(50, 100, self.world_size, self.batch, self.foreground, self.space) if not self.asteroids: print("None of a particular resource on this asteroid belt, that'd be unfair. Trying again.") self.end(Main()) return self.home_world = choice([asteroid for asteroid in self.asteroids if asteroid.position.y > self.world_size.height/4*3]) self.home_world.type = "home" self.home_world.populated = True x, y = self.home_world.position self.camera.move(Vector(x-self.size.width/2, y-self.size.height/2)) # Let's make stuff a bit more interesting. for asteroid in self.asteroids: if not asteroid.type == "home": asteroid.body.apply_impulse((triangular(-20000, 20000, 0), triangular(-20000, 20000, 0))) x, y = self.home_world.position self.player = Person(x+150, y+150, self.batch, self.playerg, self.space) self.mouse = x+150, y+150 centre = Vector(self.size.width/2, self.size.height/2) image = centre_image(resource.image("logo.png")) self.logo = sprite.Sprite(image, centre.x, centre.y, batch=self.batch, group=self.ui) self.logo.opacity = 255 self.fade = True self.faded = False planet = centre_image(resource.image("planet.png")) x = self.world_size.width/2 y = planet.height/2 self.planet_sprite = sprite.Sprite(planet, x, y, batch=self.batch, group=self.world_ui) self.win_box = BB(x-200, y-200, x+200, y+200) #self.tools = sorted([tool(self.space) for tool in Tool.__subclasses__()], key=attrgetter("order"), reverse=True) #self.buttons = {tool: Button(30, 30+number*50, tool.image, tool.description, self.use_tool(tool), self.ui, self.batch) for number, tool in enumerate(self.tools)} self.constraints = set()