from CSAir_json import *
from CSAir_helpers import *
import webbrowser
import math
from flask import Markup
def list_locations(graph):
"""
Prints all of the cities stored in the graph.
:param graph: the directed graph
"""
cities = graph.get_nodes()
for city_node in cities:
city_node.get_object().brief_print()
def lookup(graph, code):
"""
Prints data for a specific city.
:param graph: the directed graph
:param code: the airport identifier for the city
:return: True if the city was found, False otherwise
"""
city_node = graph.find_node(code)
if city_node is None:
return "Location, " + code + ", not found.\n"
else:
ret_str = city_node.get_object().get_info()
ret_str += "\nRoutes departing from " + code + ":\n"
edges = graph.get_edges_for_node(city_node)
for edge in edges:
dest_node = edge.get_destination()
ret_str += code + " to " + Markup("<a href=\"?code=" + str(dest_node) + "\">" + str(dest_node) + "</a>") \
+ ", distance: " + edge + Markup(' <button type="button" class="btn btn-primary btn-xs" '
'onclick="window.location.href=\'routes?route=' + code + '-'
+ dest_node + '\'">View route</button>') + '\n'
if len(edges) == 0:
ret_str += "No routes."
return ret_str
def generate_map_url(graph):
"""
Returns a url to display a map at gcmap.com
:param graph: the directed graph
:return: string containing the url
"""
edges = graph.get_edges()
url_string = "http://www.gcmap.com/map?P="
for edge in edges:
url_string += (edge.get_source() + "-" + edge.get_destination() + "%0D%0A")
return url_string + "&MS=wls&MR=1800&MX=720x360&PM=*"
def show_map(url_string):
"""
Launches the default web browser to the specified url.
:param url_string: a string containing a url
"""
print(url_string)
webbrowser.open(url_string)
def print_longest(graph):
"""
Prints the longest flight path
:param graph: the directed graph
"""
edges = graph.get_edges()
longest_edge = 0
longest_int = 0
for edge in edges:
if int(edge) > longest_int:
longest_edge = edge
longest_int = int(edge)
return "Longest flight: " + longest_edge.get_source() + " to " + longest_edge.get_destination() \
+ ". " + longest_edge + "km"
def print_shortest(graph):
"""
Prints the shortest flight path
:param graph: the directed graph
"""
edges = graph.get_edges()
shortest_edge = 0
shortest_int = 0
for edge in edges:
if shortest_int == 0 or int(edge) < shortest_int:
shortest_edge = edge
shortest_int = int(edge)
return "Shortest flight: " + shortest_edge.get_source() + " to " + shortest_edge.get_destination() \
+ ". " + shortest_edge + "km"
def print_average(graph):
"""
Prints the average size of all routes and the total number of routes
:param graph: the directed graph
"""
edges = graph.get_edges()
total = "Total routes: " + str(len(edges)) + "\n"
total_distance = 0
for edge in edges:
total_distance += int(edge)
average = total_distance/len(edges)
return total + "Average distance across all routes: " + str(math.floor(average)) + "km"
def print_smallest_city(graph):
"""
Prints the smallest city
:param graph: the directed graph
"""
nodes = graph.get_nodes()
smallest_pop = 0
for node in nodes:
node_pop = node.get_object().population
if smallest_pop == 0 or node_pop < smallest_pop:
smallest_city = node
smallest_pop = node_pop
ret_str = "Smallest city: " + smallest_city.get_object().get_brief()
ret_str += "Population: " + str(smallest_pop)
return ret_str
def print_largest_city(graph):
"""
Prints the largest city
:param graph: the directed graph
"""
nodes = graph.get_nodes()
largest_pop = 0
for node in nodes:
node_pop = node.get_object().population
if node_pop > largest_pop:
largest_city = node
largest_pop = node_pop
ret_str = "Largest city: " + largest_city.get_object().get_brief()
ret_str += "Population: " + str(largest_pop)
return ret_str
def print_average_city(graph):
"""
Prints the average size of all cities and the total number of cities
:param graph: the directed graph
"""
nodes = graph.get_nodes()
ret_str = "Total cities: " + str(len(nodes)) + "\n"
total_pop = 0
for node in nodes:
node_pop = node.get_object().population
total_pop += node_pop
average = total_pop/len(nodes)
return ret_str + "Average city size: " + str(math.floor(average))
def print_continents(graph):
"""
Prints the continents and cities within them
:param graph: the directed graph
"""
nodes = graph.get_nodes()
continents = [] # continents is a list of continent names.
cities = [] # cities[i] is a list of cities in continent i
for node in nodes:
city = node.get_object()
node_cont = city.continent
if node_cont not in continents:
continents.append(node_cont)
cities.append([])
for i, _ in enumerate(continents):
if continents[i] == node_cont:
cities[i].append(city)
ret_str = ""
for i, _ in enumerate(continents):
ret_str += "\nCities in " + continents[i] + ":\n"
for city in cities[i]:
ret_str += city.get_brief()
return ret_str
def print_hubs(graph):
"""
Prints the cities ordered from largest number of connections to smallest
:param graph: the directed graph
"""
nodes = graph.get_nodes()
num_edges = []
for node in nodes:
edges = graph.get_edges_for_node(node)
num_edges.append(len(edges))
both = zip(num_edges, nodes) # zip both lists into a tuple
both = sorted(both)[::-1] # sort and reverse the tuple
ret_str = ""
for pair in both:
city = pair[1].get_object()
ret_str += str(pair[0]) + " connections from: " + city.get_brief()
return ret_str
def add_city(graph):
"""
Prompts the user to add a new city to the graph.
:return: True on success, false on failure.
"""
print("Enter the new city's information:")
code = input("Code: ").rstrip('\n')
if code in graph.get_nodes():
print("City already exists, can not add. Perhaps you meant to edit the city?")
return False
use_api = input("Do you want to look up this city using the Wunderground API? (y/n)")
if use_api == "y":
return json_api_request(graph, code)
else:
name = input("Name: ").rstrip('\n')
country = input("Country: ").rstrip('\n')
continent = input("Continent: ").rstrip('\n')
timezone = input("Timezone: ").rstrip('\n')
latitude = input("Latitude: ").rstrip('\n')
longitude = input("Longitude: ").rstrip('\n')
population = input("Population: ").rstrip('\n')
region = input("Region: ").rstrip('\n')
add_city_helper(graph, code, name, country, continent, timezone, latitude, longitude, population, region)
return True
def add_route(graph):
"""
Prompts the user to add a new route.
"""
print("Enter the new route's information:")
source = input("Source: ").rstrip('\n')
dest = input("Destination: ").rstrip('\n')
distance = input("Distance: ").rstrip('\n')
direction = input("Add the route in both directions? (y/n): ").rstrip('\n')
add_route_helper(graph, source, dest, distance, direction)
def remove_city(graph):
"""
Prompts the user to remove a city.
"""
print("Enter the code of the city to remove:")
code = input("Code: ").rstrip('\n')
if graph.remove_node(code):
print("City removed.")
else:
print("Unable to remove city. A city with the specified code does not exist.")
def remove_route(graph):
"""
Prompts the user to remove a route.
"""
print("Enter the source and destination codes for the route to remove:")
source = input("Source: ").rstrip('\n')
dest = input("Destination: ").rstrip('\n')
direction = input("Remove the route in both directions? (y/n): ").rstrip('\n')
remove_route_helper(graph, source, dest, direction)
def edit_city(graph, code):
"""
Prompts the user to edit a city.
:param code: The city to edit
:return: True if edited, false otherwise.
"""
node = graph.find_node(code)
if node is None:
print("City not found, can not edit.")
return False
city = node.get_object()
print("Editing " + code + "'s information:"
"\nThe current information will be displayed, then you can enter new information:"
"\nTo keep the current information, leave the section blank.")
code = update_data("Code", city.code)
name = update_data("Name", city.name)
country = update_data("Country", city.country)
continent = update_data("Continent", city.continent)
timezone = update_data("Timezone", str(city.timezone))
latitude = update_data("Latitude", str(city.latitude))
longitude = update_data("Longitude", str(city.longitude))
population = update_data("Population", str(city.population))
region = update_data("Region", str(city.region))
graph.remove_node(node)
if not add_city_helper(graph, code, name, country, continent, timezone, latitude, longitude, population, region):
#if modifying the city failed, add the old one back to the graph.
graph.add_node(node)
return False
#edit current routes to match new information!
update_routes(graph, code, node)
return True
def route_info(graph):
"""
Prompts the user to enter a list of cities, then will display cost, time, and distance for the route.
:return: False on error, True otherwise.
"""
try:
num_cities = int(input("Total number of cities in the route: "))
except ValueError:
print("Error: Number of cities must be an integer.")
return False
if num_cities <= 0:
print("Error: The number of cities in the route must be greater than 0.")
return False
nodes = graph.get_nodes()
print("Enter the city codes for the route. Type cancel to return back to the main prompt.")
route = []
for i in range(num_cities):
code = input(":")
if code == "cancel":
return True
elif code not in nodes:
print("Error: " + code + " is not a city in our current data.")
return False
route.append(code)
return route_helper(graph, route)
def route_shortest(graph):
"""
Uses Dijkstra's algorithm to determine the shortest route between souce city and destination city.
:return: False on error, true otherwise
"""
print("Enter the city codes for the route.")
source = input("Source city: ")
dest = input("Destination city: ")
route = graph.dijkstra(source, dest)
if route is None:
return False
return route_helper(graph, route)