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_json.py
from CSAir_helpers import *
import json
from Location import Location
from Graph import DiGraph
import re
import requests


def load_initial_json(prompt=True):
    """Refactored initial prompt for query to this helper function"""
    filename_to_load = 'map_data.json'
    if prompt is False:
        graph = parse_json(filename_to_load)
        return graph

    graph = False
    while graph is False:
        filename_to_load = update_data("Enter the filename to load.\n"
                                       "If left blank, will load the default file", filename_to_load)

        graph = parse_json(filename_to_load)
        if graph is False:
            print("Could not load file. Try again.")

    return graph


def parse_json(filename):
    """
    Parses a correctly formatted JSON file and returns a graph.
    :param filename: The path to the JSON file
    :return: Graph directed graph, False on failure
    """
    graph = DiGraph.DiGraph()

    try:
        json_data = open(filename)
    except FileNotFoundError:
        return False

    try:
        data = json.load(json_data)
    except ValueError:
        return False
    json_data.close()

    try:  # check if the JSON is directed or undirected
        directed = data['directed']
    except KeyError:
        directed = False

    for city_node in data["metros"]:  # add cities

        first_cardinal = list(city_node['coordinates'])[0]  # get cardinal directions
        second_cardinal = list(city_node['coordinates'])[1]

        first_coordinate = city_node['coordinates'][first_cardinal]  # get lat/lon number values
        second_coordinate = city_node['coordinates'][second_cardinal]

        if first_cardinal == 'W' or first_cardinal == 'E':  # assign lat/lon
            latitude = str(second_coordinate) + second_cardinal
            longitude = str(first_coordinate) + first_cardinal
        else:
            latitude = str(first_coordinate) + first_cardinal
            longitude = str(second_coordinate) + second_cardinal

        loc = Location(city_node['code'], city_node['name'], city_node['country'], city_node['continent'],
                       city_node['timezone'], latitude, longitude, city_node['population'], city_node['region'])

        graph.add_node(loc, city_node['code'])

    for route in data["routes"]:  # add routes
        source_node = route['ports'][0]
        dest_node = route['ports'][1]

        graph.add_edge(source_node, dest_node, route['distance'])  # Add first directed edge
        if directed is False:
            graph.add_edge(dest_node, source_node, route['distance'])  # Add second directed edge

    return graph


def save_json(graph, filename):
    """
    Saves the graph to a JSON file
    :param graph: The graph
    :param filename: Name of the file to write to
    """
    edges = graph.get_edges()

    directed = check_directed(graph)  # Check if the JSON needs to be saved as directed.
    if not directed:
        edges = graph.get_undirected_edges()

    data = {'directed': directed, 'data sources': [], 'metros': [], 'routes': []}  # begin building the JSON data object

    for edge in edges:  # add edges
        new_route = {'ports': [edge.get_source(), edge.get_destination()], 'distance': edge}
        data['routes'].append(new_route)

    nodes = graph.get_nodes()
    for node in nodes:  # add cities
        city = node.get_object()  # get Location object from node

        lat_letter = re.sub(r'\d+', "", city.latitude)  # get the cardinal letter
        lat_num = re.findall(r'\d+', city.latitude)  # get the coordinate number

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

        coordinates = {lat_letter: lat_num, lon_letter: lon_num}  # build coordinates
        new_metro = {'code': city.code, 'name': city.name,
                     'country': city.country, 'continent': city.continent,
                     'timezone': city.timezone, 'coordinates': coordinates,
                     'population': city.population, 'region': city.region}  # build city

        data['metros'].append(new_metro)  # add city

    try:  # save JSON file
        file = open(filename, "w")
    except OSError:
        return False
    file.write(json.dumps(data, sort_keys=True, indent=4, separators=(',', ': ')))
    file.close()
    print("Data saved to " + filename)
    return True


def json_api_request(graph, code):
    """
    Make an API request to Wunderground to obtain data for a city given the airport code.
    :param graph: The graph
    :param code: Airport/city code
    """
    #make request, load JSON, then parse JSON
    url = 'http://api.wunderground.com/api/82385a8fea1e0a46/geolookup/q/' + code + '.json'
    print(url)
    resp = requests.get(url=url)  # using HTTP Requests for humans: http://docs.python-requests.org/en/latest/
    data = json.loads(resp.text)

    try:
        country = data['location']['country_name']
    except KeyError:
        return "Error"

    latitude = int(float(data['location']['lat']))
    longitude = int(float(data['location']['lon']))
    if latitude < 0:
        latitude = str(abs(latitude)) + "S"
    else:
        latitude = str(abs(latitude)) + "N"

    if longitude < 0:
        longitude = str(abs(longitude)) + "W"
    else:
        longitude = str(abs(longitude)) + "E"

    timezone = data['location']['tz_long']
    name = data['location']['city']
    continent = country
    population = 0
    region = 0
    if country == "USA":
        region = 1
        continent = "North America"

    print("Note: The API does not have population, continent, or region data.\n"
          "You will need to edit the city to add it.\n")

    if "Error" not in add_city_helper(graph, code, name, country, continent, timezone, latitude, longitude, population, region):
        return True
    else:
        return False


def check_directed(graph):
    """
    Check if it is necessary that the graph is represented as a directed graph when saved to a JSON file.
    :param graph: The graph
    :return: True if the graph must be represented as directed, false otherwise.
    """
    edges = graph.get_edges()
    #check that each edge has an identical edge in the opposite direction
    for edge in edges:
        source = edge.get_source()
        dest = edge.get_destination()
        dest_edges = graph.get_edges_for_node(dest)

        found = False
        for d_edge in dest_edges:
            if d_edge.get_destination() == source:
                found = True

        if not found:
            print("Graph needs to be saved as directed.")
            return True

    return False


def merge_json(graph, filename):
    """
    Merge a new JSON file into the graph
    :param graph: The graph
    :param filename: new JSON file.
    :return: True on success, false otherwise
    """
    other_graph = parse_json(filename)
    if other_graph is False:
        print("Could not load data, " + filename + " does not exist.")
        return False

    graph.merge(other_graph)
    print("Merged " + filename + " into current data.")
    return True