def test_find_best_match(): ab_pos = BloodType(BloodTypeLetter.AB, BloodTypePolarity.POS) o_neg = BloodType(BloodTypeLetter.O, BloodTypePolarity.NEG) wait_list = WaitList() patient1 = Patient('name', 'illness 1', OrganType.Pancreas, ab_pos, 100, 1, wait_list) patient2 = Patient('name', 'illness 2', OrganType.Pancreas, ab_pos, 250, 3, wait_list) patient3 = Patient('name', 'illness 3', OrganType.Pancreas, o_neg, 400, 2, wait_list) patient4 = Patient('name', 'illness 4', OrganType.Heart, ab_pos, 500, 2, wait_list) organ = Organ(OrganType.Pancreas, o_neg, 1) node1 = Node(1, adjacency_dict={2: {'weight': 10, 'status': True}, 3: {'weight': 25, 'status': True}}) node2 = Node(2, adjacency_dict={3: {'weight': 35, 'status': True}}) node3 = Node(3) test_net = Network({1: node1, 2: node2, 3: node3}) weights, paths = Dijkstra.dijkstra(test_net, 1) patient = OrganAllocator.find_best_match(organ, wait_list, weights) assert patient is patient3 assert len(wait_list.wait_list) is 4 node1.adjacency_dict[3]['weight'] = 300 node3.adjacency_dict[1]['weight'] = 300 test_net.mark_node_inactive(2) wait_list.remove_patient(patient3) weights, paths = Dijkstra.dijkstra(test_net, 1) assert len(weights) is 2 patient = OrganAllocator.find_best_match(organ, wait_list, weights) assert patient is patient1
def allocate_organs(organ_list: OrganList, wait_list: WaitList, network: Network) -> None: """ Allocates organs from an OrganList to patients on a WaitList in a given Network. Organs are allocated to the patient with the highest priority within a a suitable proximity. The proximity is considered suitable iff (organ viability) - (cost of travel) >= 10 :param OrganList organ_list: list of organs available for transplant :param WaitList wait_list: list of patients in need of transplant :param Network network: hospital network patients and organs are present in """ # ANSI codes to emphasize output bold, reset = '\033[1m', '\033[0m' source, recipient = None, None for organ in organ_list.organ_list: if organ.origin_location is source: recipient = OrganAllocator.find_best_match( organ, wait_list, dijkstra.weight) # type: ignore else: source = organ.origin_location dijkstra = Dijkstra(network, source) recipient = OrganAllocator.find_best_match( organ, wait_list, dijkstra.weight) if recipient: organ.move_organ(recipient.location, dijkstra.weight[recipient.location], dijkstra.shortest_path(recipient.location)) wait_list.remove_patient(recipient) print(f'\n{bold}The following pair have been united:{reset}' f'\n{recipient.__str__()}{organ.__str__()}') # organ is always removed (either matched, or exceeds max viability) organ_list.empty_list()
def generate_patients_to_list(graph: Network, n: int, wait_list: WaitList) -> None: """ Generates N patients and add all generated patients to a WaitList :param Network graph: network where patients can be generated :param int n: number of patients to generate :param WaitList wait_list: list of patients to add generated patients to """ wait_list.add_patients(PatientGenerator.generate_patients(graph, n))
def generate_random_graphs(path='.'): for x in range(1, 4): with shelve.open(join(path, f'random_networks{x}')) as db: db.clear() network = GraphBuilder.graph_builder(n=150, max_weight=25) patients = WaitList() PatientGenerator.generate_patients_to_list(graph=network, n=300, wait_list=patients) organs = OrganList() OrganGenerator.generate_organs_to_list(graph=network, n=75, organ_list=organs) hospital_network = network patient_network = SubnetworkGenerator.generate_subnetwork( network=hospital_network, collection=patients) organ_network = SubnetworkGenerator.generate_subnetwork( network=hospital_network, collection=organs) db[f'hospital_network{x}'] = hospital_network db[f'patient_network{x}'] = patient_network db[f'organ_network{x}'] = organ_network
def test_allocate_organs(): ab_pos = BloodType(BloodTypeLetter.AB, BloodTypePolarity.POS) o_neg = BloodType(BloodTypeLetter.O, BloodTypePolarity.NEG) organ_list = OrganList() organ1 = Organ(OrganType.Pancreas, ab_pos, 1, organ_list) organ2 = Organ(OrganType.Heart, o_neg, 1, organ_list) wait_list = WaitList() patient1 = Patient('name', 'illness 1', OrganType.Pancreas, ab_pos, 100, 1, wait_list) patient2 = Patient('name', 'illness 2', OrganType.Pancreas, o_neg, 200, 3, wait_list) patient3 = Patient('name', 'illness 3', OrganType.Heart, ab_pos, 100, 2, wait_list) patient4 = Patient('name', 'illness 4', OrganType.Heart, ab_pos, 50, 2, wait_list) node1 = Node(1, adjacency_dict={2: {'weight': 10, 'status': True}, 3: {'weight': 25, 'status': True}}) node2 = Node(2, adjacency_dict={3: {'weight': 35, 'status': True}}) node3 = Node(3) test_net = Network({1: node1, 2: node2, 3: node3}) OrganAllocator.allocate_organs(organ_list, wait_list, test_net) assert len(organ_list.organ_list) is 0 assert len(wait_list.wait_list) is 2 assert patient2 in wait_list.wait_list assert patient4 in wait_list.wait_list
def find_best_match(organ: Organ, wait_list: WaitList, weights: Dict[int, float]) -> Optional[Patient]: """ Finds the most optimal patient match for a given organ. The optimal match is the patient with the highest priority rating of compatible organ_need/blood_type who is within a tolerable distance (for organ viability) :param Organ organ: individual organ which is being matched with the optimal patient :param WaitList wait_list: list of patients in need of a transplant :param dict weights: cumulative weights of shortest paths from source node (organ location) to all other nodes in the network :return: Patient representing the most optimal match """ # ANSI codes to emphasize output bold_red, red, reset = '\033[31;1m', '\033[31m', '\033[0m' matches = wait_list.get_prioritized_patients(organ) # returns the patient with the highest priority within acceptable proximity while len(matches) != 0: patient = heapq._heappop_max(matches) # type: ignore if organ.viability >= weights[patient.location] - 10: return patient # in the event there are no matches print(f'\n{bold_red}The following organ has no suitable matches:' f'\n{red}{organ.__str__()}{reset}') return None
def reset_network(): """ Resets the network by clearing the organ list and wait list variables. This leaves an empty network to continue interacting with. """ global organ_list global wait_list organ_list = OrganList() wait_list = WaitList() print(f'\n{ANSI_BOLD}The network has been reset. There are no ' f'patients or organs remaining in the network.{ANSI_RESET}\n')
def test_generate_patients_to_list(): wait_list = WaitList() PatientGenerator.generate_patients_to_list(graph=test_net, n=n, wait_list=wait_list) assert len(wait_list.wait_list) is n for patient in wait_list.wait_list: assert patient.location in test_net.nodes() assert 0 <= patient.organ_needed.value <= 5 assert 0 <= patient.blood_type.blood_type_letter.value <= 3 assert 0 <= patient.blood_type.blood_type_polarity.value <= 1 assert 0 <= patient.priority < 100 + n assert patient.location in test_net.nodes()
def restart(): """ Clears all data from the system. This removes the existing network, wait list, and organ list. """ global network global organ_list global wait_list network = None organ_list = OrganList() wait_list = WaitList() print(f'\n{ANSI_BOLD}The system has been reset. There is no network,' f' patients, or organs.{ANSI_RESET}\n')
def build_network(): """ Builds a network passing console input as GraphBuilder parameters (N nodes). If a network already exists, a confirmation message verifies the user wants to clear the existing data. """ global network global wait_list global organ_list if network: response = input(f'\nThere is already an existing network!\n' f'Would you like to clear the network? ' f'(this will clear patient and organ lists as well)\n' f'{ANSI_YELLOW}(y/n): {ANSI_RESET}') if response.lower() == 'y': wait_list = WaitList() organ_list = OrganList() elif response.lower() == 'n': print() return else: print(f'\n{ANSI_BOLD}Unrecognized selection. Returning to ' f'main menu.{ANSI_RESET}\n') return try: response = int( input(f'\nEnter the number of hospitals (nodes) ' f'you\'d like in the network: ')) network = GraphBuilder.graph_builder(response) except ValueError: print(f'\n{ANSI_RED_BOLD}ValueError:{ANSI_RED} valid values ' f'are ints >= 4{ANSI_RESET}\n') return # network has been generated response = input(f'\nA network has been built with {response} nodes. ' f'Would you like to print the network to the console?' f'\n{ANSI_YELLOW}(y/n): {ANSI_RESET}') if response.lower() == 'y': print(network) elif response.lower() == 'n': print() return else: print(f'\n{ANSI_BOLD}Unrecognized selection. Returning to ' f'main menu.\n{ANSI_RESET}')
# 2: {'weight': 4, 'status': True}}) # node5 = Node(5, "E", # {1: {'weight': 1, 'status': True}, # 4: {'weight': 2, 'status': True}}) # # net = Network({node1.node_id: node1, # node2.node_id: node2, # node3.node_id: node3, # node4.node_id: node4, # node5.node_id: node5}) # creates network network = GraphBuilder.graph_builder(20) # creates patient list wait_list = WaitList() PatientGenerator.generate_patients_to_list(network, 10, wait_list) print(wait_list) # creates organ list organ_list = OrganList() OrganGenerator.generate_organs_to_list(network, 10, organ_list) print(organ_list) # creates subnetworks for patients and organs patient_network = SubnetworkGenerator.generate_subnetwork(network, wait_list) organ_network = SubnetworkGenerator.generate_subnetwork(network, organ_list) # generates NetworkX graphs graph_nx = GraphConverter.convert_to_networkx(network) patient_nx = GraphConverter.convert_to_networkx(patient_network)
from network_simulator.GraphBuilder import GraphBuilder from network_simulator.OrganAllocator import OrganAllocator from network_simulator.OrganGenerator import OrganGenerator from network_simulator.OrganList import OrganList from network_simulator.PatientGenerator import PatientGenerator from network_simulator.WaitList import WaitList network, wait_list, organ_list = None, WaitList(), OrganList() ANSI_YELLOW, ANSI_YELLOW_BOLD, ANSI_RED = '\033[33m', '\033[33;1m', '\033[31m' ANSI_RED_BOLD, ANSI_BOLD, ANSI_RESET = '\033[31;1m', '\033[1m', '\033[0m' def print_menu(): """ Prints menu options """ print( f'{ANSI_YELLOW}Main Menu:{ANSI_RESET}\n' f'\t{ANSI_YELLOW}1 -{ANSI_RESET} Generate Network\n' f'\t{ANSI_YELLOW}2 -{ANSI_RESET} Generate Patients\n' f'\t{ANSI_YELLOW}3 -{ANSI_RESET} Harvest Organs (and allocate)\n' f'\t{ANSI_YELLOW}4 -{ANSI_RESET} Reset Network (clears list of patients/organs)\n' f'\t{ANSI_YELLOW}5 -{ANSI_RESET} Restart\n' f'\t{ANSI_YELLOW}0 -{ANSI_RESET} Exit\n') def main_menu(): """ Control structure executes each action corresponding to menu items. Loops until user enters 0 to exit. """