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_helpers.py
import re
from Location import Location
cruise_speed = 208.333333  # 750km/h in m/s


def add_city_helper(graph, code, name, country, continent, timezone, latitude, longitude, population, region):
    """
    Helper function for add_city and edit_city
    :return: True if city added, false otherwise
    """
    failed = False  # flag to indicate a failure and exit after error messages are printed.
    lat_letter = re.sub(r'\d+', "", latitude)  # get the cardinal letter
    lat_num = re.findall(r'\d+', latitude)  # get the coordinate number

    lon_letter = re.sub(r'\d+', "", longitude)
    lon_num = re.findall(r'\d+', longitude)

    return_string = ""

    try:
        population = int(population)
        if population < 0:  # population sanity check
            return_string += "Error: Unable to add city, Population can not be negative." + '\n'
            failed = True
    except ValueError:
        return_string += "Error: Unable to add city, population must be a number." + '\n'
        failed = True

    #check to make sure latitude is correctly N or S, and has a number
    if (lat_letter != "N" and lat_letter != "S") or not lat_num:
        return_string += "Error: Invalid latitude letter (" + lat_letter + ") or number (" + str(lat_num) + ")" + '\n'
        failed = True

    #check to make sure longitude is correctly W or E, and has a number
    if (lon_letter != "W" and lon_letter != "E") or not lon_num:
        return_string += "Error: Invalid longitude letter (" + lon_letter + ") or number (" + str(lon_num) + ")" + '\n'
        failed = True

    if failed:
        return return_string

    #add the new city
    new_city = Location(code, name, country, continent, timezone, latitude, longitude, population, region)
    if graph.add_node(new_city, code):
        return_string += "New city added successfully: " + code + '\n'
        #new_city.return_string += )
        return return_string
    else:
        return_string += "Error: Unable to add city, a city with the same code already exists." + '\n'
        return return_string


def add_route_helper(graph, source, dest, distance, direction):
    """
    Helper function for add_route
    :return: True if added, false otherwise.
    """
    if graph.find_node(source) is None or graph.find_node(dest) is None:
        print("Unable to add route. One or both cities not found.")
        return False

    graph.add_edge(source, dest, distance)  # add route
    print("Route added ", end="")
    if direction == "y":
        graph.add_edge(dest, source, distance)  # add route in second direction if user specified
        print("in both directions.")

    print("")
    return True


def remove_route_helper(graph, source, dest, direction):
    """
    Helper function for remove_route
    :return: True if route was removed, false otherwise.
    """
    edges = graph.get_edges()  # list of edges

    edges_to_remove = []  # list of edges to remove
    for edge in edges:  # check if edge matches source/dest. Also checks for edge in second direction.
        if (edge.get_destination() == dest and edge.get_source() == source) \
                or (edge.get_destination() == source and edge.get_source() == dest):
            edges_to_remove.append(edge)  # add to edges to remove

    for edge in edges_to_remove:  # remove edges from graph.
        graph.remove_edge(edge.get_source(), edge.get_destination())

    if len(edges_to_remove) > 0:
        if direction == "n":
            graph.add_edge(dest, source, edges_to_remove[0])

        return True

    print("Unable to remove route. Route does not exist.")
    return False


def time_for_acceleration(distance):
    """
    Calculates time to accelerate to 750km/h over given distance.
    Using: time = 2*distance(m) / final velocity (m/s)
    :param distance: distance (in meters)
    :return: time (in seconds)
    """
    return (2*distance)/cruise_speed


def seconds_to_formatted_time(seconds):
    """
    Convert seconds to a formatted time string.
    :param seconds: int of seconds
    :return: formatted string.
    """
    hours = seconds/60/60
    minutes = (seconds/60) % 60

    hour_str = "%d" % hours
    hour_str += ":%d" % minutes
    if minutes < 10:
        hour_str += "0"

    return hour_str


def update_routes(graph, new_code, old_code):
    """
    Helper function for edit_city
    Updates routes for a renamed city to the new city code
    :param new_code: new code of city.
    :param old_code: old code of city.
    """
    edges_to_update_dest = []  # lists of edges where the renamed node is source or destination
    edges_to_update_source = []

    edges = graph.get_edges()
    for edge in edges:  # add edges to update to the update lists
        if edge.get_destination() == old_code:
            edges_to_update_dest.append(edge)
        elif edge.get_source() == old_code:
            edges_to_update_source.append(edge)

    graph.remove_edges_containing_node(old_code)  # remove edges containing the old node.

    for edge in edges_to_update_dest:  # update edges to the new code
            graph.add_edge(edge.get_source(), new_code, edge)  # add updated edge

    for edge in edges_to_update_source:
            graph.add_edge(new_code, edge.get_destination(), edge)


def update_data(message_str, data):
    """
    Takes input from the user to update data. If the user enters a blank string, data remains the same.
    :param message_str: Message to display to the user
    :param data: object to update.
    :return: the updated data
    """
    result = input(message_str + " (" + data + "): ").rstrip('\n')
    if result == "":
        result = data

    return result


def generate_route_list(route_str):
    """
    Generate list of nodes in the route.
    :param route_str: String of city codes separated by hyphens '-'
    :return: List containing the route
    """
    if route_str is not None:
        route_list = route_str.split("-")
    else:
        route_list = None

    return route_list


def generate_route_string(route_list):
    """
    Generate a string containing the route in the input list.
    :param route_list: List containing the route
    :return: String of city codes separated by hyphens '-'
    """
    route_str = ""
    for node in route_list[0:-1]:
        route_str += node + "-"

    return route_str + route_list[-1]  # append dest node and return


def route_helper(graph, route):
    """
    Helper function for route_shortest and route_info.
    Calculates and displays cost, time, and distance for the route.
    :param graph: The graph.
    :param route: List containing nodes for the route
    :return: True on success, False on failure.
    """
    return_string = ""
    total_distance = 0
    cost = 0  # total cost
    cost_multiplier = 0.35  # starting cost multiplier for leg
    time = 0  # total time
    for i, _ in enumerate(route[0:-1]):  # check all nodes in the route
        edge = graph.get_edge_from_nodes(route[i], route[i+1])  # get edge between two nodes on route

        if edge is None:  # verify that connection exists
            return_string += "Error: no flight from: " + route[i] + " to " + route[i+1] + '\n'
            return return_string

        leg_distance = int(edge)  # get distance of edge
        total_distance += leg_distance  # sum distances

        leg_cost = leg_distance*cost_multiplier  # leg base cost
        cost += leg_cost  # sum costs
        if cost_multiplier > 0:  # reduce leg cost multiplier
            cost_multiplier -= 0.05
            cost_multiplier = round(cost_multiplier, 3)

        leg_time = calc_leg_time(leg_distance)  # calculate leg flight time

        return_string += "\nLeg " + route[i] + " to " + route[i+1] + '\n'  # print leg information
        return_string += "Leg distance: " + str(leg_distance) + "km" + '\n'
        return_string += "Leg cost: $%.2f" % leg_cost + '\n'
        return_string += "Leg flight time: " + seconds_to_formatted_time(leg_time)+ '\n'

        if i > 0:  # calculate layover time
            leg_time += calc_layover_time(graph, route[i])

        time += leg_time  # sum time

    return_string += "\n Route: " + generate_route_string(route) + '\n'   # print route information
    return_string += "Total distance: " + str(total_distance) + "km" + '\n'
    return_string += "Total cost: $%.2f" % cost + '\n'
    return_string += "Total time: " + seconds_to_formatted_time(time) + '\n'
    return return_string


def calc_leg_time(leg_distance):
    """
    Calculate time for a leg given it's distance.
    :param leg_distance: distance (km)
    :return: time in seconds
    """
    leg_time = 0
    #if dist > 400km, accelerate to 750km/h for first 200km, fly at 750km/h, then decelerate to 0 for last 200km
    if leg_distance >= 400:
        leg_time += time_for_acceleration(200000)*2  # time for acceleration from 0 to 750km/h and back to 0
        leg_time += ((leg_distance-400)*1000)*(1/cruise_speed)  # cruise time
    else:  # if dist < 400km, accelerate to 750km/h for first half then decelerate to 0
        leg_time += time_for_acceleration((leg_distance/2)*1000)*2

    return leg_time


def calc_layover_time(graph, node):
    """
    Calculate layover time for a node.
    :return: time in seconds
    """
    num_outbound = len(graph.get_edges_for_node(node))
    minutes_for_layover = 120 - 10*(num_outbound-1)
    print("Layover time at: " + node + ": " + seconds_to_formatted_time(minutes_for_layover*60))
    return minutes_for_layover*60