CSAir

.idea
    .name 13 B
    Assignment2.0.iml 295 B
    dictionaries
       lantay77.xml 357 B
    encodings.xml 171 B
    misc.xml 348 B
    modules.xml 289 B
    runConfigurations
       Server.xml 1008 B
       Unittests_in_Tests.xml 1 KB
    scopes
       scope_settings.xml 143 B
    vcs.xml 189 B
    workspace.xml 59 KB
CSAir.py 11 KB
CSAir_helpers.py 9 KB
CSAir_json.py 7 KB
CSAir_server.py 5 KB
Graph
    DiGraph.py 9 KB
    Edge.py 518 B
    Node.py 360 B
Location.py 1 KB
ManualTestPlan.odt 441 KB
ManualTestPlan.pdf 1029 KB
Query.py 2 KB
README 499 B
Server.py 8 KB
Tests
    expected
       brief_print_expected.txt 13 B
       continents_expected.txt 58 B
       expected.json 1 KB
       hubs_expected.txt 70 B
       list_expected.txt 28 B
       longest_expected.txt 36 B
       other_expected.txt 205 B
       print_expected.txt 130 B
       shortest_expected.txt 37 B
    invalid.json 15 KB
    out
       brief_print_test.txt 13 B
       continents_test.txt 58 B
       hubs_test.txt 70 B
       list_test.txt 28 B
       longest_test.txt 36 B
       other_test.txt 205 B
       out.json 1 KB
       print_test.txt 130 B
       shortest_test.txt 37 B
    testCSAir.py 3 KB
    testFunctionsForServer.py 1 KB
    testGraph.py 1 KB
    testLocation.py 938 B
    testServer.py 6 KB
    test_2.1.py 8 KB
    valid.json 871 B
    valid2.json 582 B
add_city_post.py 800 B
changes 633 B
cmi_hub.json 1 KB
map_data.json 15 KB
map_data_r2.json 15 KB
static
    bootstrap.js 59 KB
    css
       bootstrap-theme.css 20 KB
       bootstrap-theme.css.map 22 KB
       bootstrap-theme.min.css 18 KB
       bootstrap.css 129 KB
       bootstrap.css.map 215 KB
       bootstrap.min.css 106 KB
       starter-template.css 186 B
    fonts
       glyphicons-halflings-regular.eot 19 KB
       glyphicons-halflings-regular.svg 61 KB
       glyphicons-halflings-regular.ttf 40 KB
       glyphicons-halflings-regular.woff 22 KB
    js
       bootstrap.js 59 KB
       bootstrap.min.js 31 KB
templates
    city.html 13 KB
    routes.html 10 KB
uploads
CSAir.py
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)