Example #1
0
def fixDisowned(request):
    """ Respond to the "/geo/editor/sanitycheck/fixdisowned" URL.

        We fix any "disowned" locations -- locations where the parent/child,
        child/parent, or neighbour/neighbour relations aren't symmetrical.

        In particular:

          * If a location has a parent which doesn't list the locaiton as a
            child, a child entry will be added to the parent.

          * If a location has a child which doesn't list the location as a
            parent, a parent entry will be added to the child.

          * If a location has a neighbour which doesn't list the location as a
            neighbour, a neighbour entry will be added to the neighbour.
    """
    # Display a "please wait" message while we work.

    if not pleasewait.shown(request):
        return pleasewait.message(
            request, "Geodatabase Editor", "Location Sanity-Checker", "Fixing disowned locations, please wait..."
        )

    # Get a connection to our database.

    cursor = connection.cursor()

    # Load the list of Level records into memory.

    levelIDToNumber = {}  # Maps Level record ID to level number.

    for level in Level.objects.all():
        levelIDToNumber[level.id] = level.level

    # Load the list of Location records into memory.

    locations = {}  # Maps record ID to a (code, level_num, name, display_name)
    # tuple.

    cursor.execute("SELECT id,code,level_id,name,display_name " + "FROM data_location")
    row = cursor.fetchone()
    while row != None:
        id, code, level_id, name, display_name = row

        level_num = levelIDToNumber[level_id]
        locations[id] = (code, level_num, name, display_name)

        row = cursor.fetchone()

    # Load the list of location parents into memory.

    location_parents = {}  # Maps record ID to a set of parent record IDs.

    cursor.execute("SELECT from_location_id,to_location_id " + "FROM data_location_parents")
    row = cursor.fetchone()
    while row != None:
        from_loc_id, to_loc_id = row

        try:
            location_parents[from_loc_id].add(to_loc_id)
        except KeyError:
            location_parents[from_loc_id] = set([to_loc_id])

        row = cursor.fetchone()

    # Load the list of location children into memory.

    location_children = {}  # Maps record ID to set of child record IDs.

    cursor.execute("SELECT from_location_id,to_location_id " + "FROM data_location_children")
    row = cursor.fetchone()
    while row != None:
        from_loc_id, to_loc_id = row

        try:
            location_children[from_loc_id].add(to_loc_id)
        except KeyError:
            location_children[from_loc_id] = set([to_loc_id])

        row = cursor.fetchone()

    # Load the list of location neighbours into memory.

    location_neighbours = {}  # Maps record ID to set of neighbour record IDs.

    cursor.execute("SELECT from_location_id,to_location_id " + "FROM data_location_neighbors")
    row = cursor.fetchone()
    while row != None:
        from_loc_id, to_loc_id = row

        try:
            location_neighbours[from_loc_id].add(to_loc_id)
        except KeyError:
            location_neighbours[from_loc_id] = set([to_loc_id])

        row = cursor.fetchone()

    # Fix any locations which have been "disowned" by their parents.  That is,
    # location A lists location B as a parent, but location B doesn't list
    # location A as a child.

    num_parents_fixed = 0
    for loc_id in locations.keys():
        if loc_id not in location_parents:
            continue  # No parents.

        for parent_id in location_parents[loc_id]:
            disowned = False
            if parent_id not in location_children:
                disowned = True
            elif loc_id not in location_children[parent_id]:
                disowned = True

            if disowned:
                location = Location.objects.get(id=loc_id)
                parent = Location.objects.get(id=parent_id)
                parent.children.add(location)
                num_parents_fixed = num_parents_fixed + 1

    # Fix any locations which have been "disowned" by their children.  That is,
    # location A lists location B as a child, but location B doesn't list
    # location A as a parent.

    num_children_fixed = 0
    for loc_id in locations.keys():
        if loc_id not in location_children:
            continue  # No children.

        for child_id in location_children[loc_id]:
            disowned = False
            if child_id not in location_parents:
                disowned = True
            elif loc_id not in location_parents[child_id]:
                disowned = True

            if disowned:
                location = Location.objects.get(id=loc_id)
                child = Location.objects.get(id=child_id)
                child.parents.add(location)
                num_children_fixed = num_children_fixed + 1

    # Fix any locations which are "disowned" by their neighbours.  That is,
    # location A lists location B as a neighbour, but location B doesn't list
    # location A as a neighbour.

    num_neighbours_fixed = 0
    for loc_id in locations.keys():
        if loc_id not in location_neighbours:
            continue  # No neighbours.

        for neighbour_id in location_neighbours[loc_id]:
            disowned = False
            if neighbour_id not in location_neighbours:
                disowned = True
            elif loc_id not in location_neighbours[neighbour_id]:
                disowned = True

            if disowned:
                location = Location.objects.get(id=loc_id)
                neighbour = Location.objects.get(id=neighbour_id)
                neighbour.neighbors.add(location)
                num_neighbours_fixed = num_neighbours_fixed + 1

    # Finally, tell the user the results.

    return render_to_response(
        "editor/templates/disowned_fixed.html",
        {
            "num_parents_fixed": num_parents_fixed,
            "num_children_fixed": num_children_fixed,
            "num_neighbours_fixed": num_neighbours_fixed,
        },
    )
Example #2
0
def main(request):
    """ Respond to the "/geo/editor/sanitycheck" request.

        We search for "suspicious" locations, and list them as possible
        problems that need fixing.

        Note that, for speed, we bypass the ORM and access the MySQL database
        directly.
    """
    # Display a "please wait" message while waiting for the results to be
    # calculated.

    if not pleasewait.shown(request):
        return pleasewait.message(
            request, "Geodatabase Editor", "Location Sanity-Checker", "Checking locations, please wait..."
        )

    # Get a connection to our database.

    cursor = connection.cursor()

    # Load the list of Level records into memory.

    levelIDToNumber = {}  # Maps Level record ID to level number.
    levelNumberToName = {}  # Maps Level number to name.

    for level in Level.objects.all():
        levelIDToNumber[level.id] = level.level
        levelNumberToName[level.level] = level.name

    # Load the list of Location records into memory.

    locations = {}  # Maps record ID to a (code, level_num, name, display_name)
    # tuple.

    cursor.execute("SELECT id,code,level_id,name,display_name " + "FROM data_location")
    row = cursor.fetchone()
    while row != None:
        id, code, level_id, name, display_name = row

        level_num = levelIDToNumber[level_id]
        locations[id] = (code, level_num, name, display_name)

        row = cursor.fetchone()

    # Load the list of location parents into memory.

    location_parents = {}  # Maps record ID to a set of parent record IDs.

    cursor.execute("SELECT from_location_id,to_location_id " + "FROM data_location_parents")
    row = cursor.fetchone()
    while row != None:
        from_loc_id, to_loc_id = row

        try:
            location_parents[from_loc_id].add(to_loc_id)
        except KeyError:
            location_parents[from_loc_id] = set([to_loc_id])

        row = cursor.fetchone()

    # Load the list of location children into memory.

    location_children = {}  # Maps record ID to set of child record IDs.

    cursor.execute("SELECT from_location_id,to_location_id " + "FROM data_location_children")
    row = cursor.fetchone()
    while row != None:
        from_loc_id, to_loc_id = row

        try:
            location_children[from_loc_id].add(to_loc_id)
        except KeyError:
            location_children[from_loc_id] = set([to_loc_id])

        row = cursor.fetchone()

    # Load the list of location neighbours into memory.

    location_neighbours = {}  # Maps record ID to set of neighbour record IDs.

    cursor.execute("SELECT from_location_id,to_location_id " + "FROM data_location_neighbors")
    row = cursor.fetchone()
    while row != None:
        from_loc_id, to_loc_id = row

        try:
            location_neighbours[from_loc_id].add(to_loc_id)
        except KeyError:
            location_neighbours[from_loc_id] = set([to_loc_id])

        row = cursor.fetchone()

    # Load the list of names into memory.

    names = {}  # Maps record ID to name.

    cursor.execute("SELECT id,name FROM data_name")
    row = cursor.fetchone()
    while row != None:
        id, name = row

        names[id] = name

        row = cursor.fetchone()

    # Load the list of names used by each location into memory.

    location_names = {}  # Maps location record ID to set of name record IDs.

    cursor.execute("select location_id,name_id FROM data_locationname")
    row = cursor.fetchone()
    while row != None:
        loc_id, name_id = row

        try:
            location_names[loc_id].add(name_id)
        except KeyError:
            location_names[loc_id] = set([name_id])

        row = cursor.fetchone()

    # Now that we've got all the information we need in memory, start looking
    # for potentially bad locations.

    bad_locs = []  # List of potentially bad locations.  Each list item is a
    # dictionary with the following entries:
    #
    #     'code'
    #
    #         The location's code.
    #
    #     'level'
    #
    #         A descriptive name for this location's level
    #
    #     'name'
    #
    #         The name for this location.
    #
    #     'problem'
    #
    #         A string describing what was wrong with the
    #         location.

    # Find any locations that aren't countries and lack parents.

    for loc_id in locations.keys():
        level_num = locations[loc_id][1]
        if level_num == 1:
            continue  # ignore countries.

        if len(location_parents.get(loc_id, [])) == 0:
            # This location has no parents -> flag it as a bad location.
            code, level_num, name, display_name = locations[loc_id]
            bad_locs.append(
                {"code": code, "level": levelNumberToName[level_num], "name": name, "problem": "lacks a parent"}
            )

    # Find any locations which lack a name.

    for loc_id in locations.keys():
        level_num = locations[loc_id][1]
        if level_num == 8:
            continue  # ZIP codes don't have names.

        if len(location_names.get(loc_id, [])) == 0:
            # This location has no name -> flag it as a bad location.
            code, level_num, name, display_name = locations[loc_id]
            bad_locs.append(
                {"code": code, "level": levelNumberToName[level_num], "name": name, "problem": "has no names"}
            )

    # Find any locations which have names ending or starting with a space.

    bad_name_ids = set()  # Set of name IDs which have bad values.
    for name_id, name in names.items():
        badName = False
        if name.startswith(" "):
            badName = True
        if name.endswith(" "):
            badName = True

        if badName:
            bad_name_ids.add(name_id)

    for loc_id, name_ids_for_loc in location_names.items():
        for bad_name_id in name_ids_for_loc.intersection(bad_name_ids):
            code, level_num, name, display_name = locations[loc_id]
            bad_name = names[bad_name_id]
            bad_locs.append(
                {
                    "code": code,
                    "level": levelNumberToName[level_num],
                    "name": name,
                    "problem": "has a name (" + bad_name + ") " + "starting or ending with a space",
                }
            )

    # Find any locations which have been "disowned" by their parents.  That is,
    # location A lists location B as a parent, but location B doesn't list
    # location A as a child.

    for loc_id in locations.keys():
        if loc_id not in location_parents:
            continue  # No parents.

        for parent_id in location_parents[loc_id]:
            disowned = False
            if parent_id not in location_children:
                disowned = True
            elif loc_id not in location_children[parent_id]:
                disowned = True

            if disowned:
                code, level_num, name, display_name = locations[loc_id]
                parent_code = locations[parent_id][0]
                parent_name = locations[parent_id][2]

                bad_locs.append(
                    {
                        "code": code,
                        "level": levelNumberToName[level_num],
                        "name": name,
                        "problem": "has been disowned by parent " + parent_name + " (" + parent_code + ")",
                    }
                )

    # Find any locations which have been "disowned" by their children.  That
    # is, location A lists location B as a child, but location B doesn't list
    # location A as a parent.

    for loc_id in locations.keys():
        if loc_id not in location_children:
            continue  # No children.

        for child_id in location_children[loc_id]:
            disowned = False
            if child_id not in location_parents:
                disowned = True
            elif loc_id not in location_parents[child_id]:
                disowned = True

            if disowned:
                code, level_num, name, display_name = locations[loc_id]
                child_code = locations[child_id][0]
                child_name = locations[child_id][2]

                bad_locs.append(
                    {
                        "code": code,
                        "level": levelNumberToName[level_num],
                        "name": name,
                        "problem": "has been disowned by child " + child_name + " (" + child_code + ")",
                    }
                )

    # Find any locations which are "disowned" by their neighbours.  That is,
    # location A lists location B as a neighbour, but location B doesn't list
    # location A as a neighbour.

    for loc_id in locations.keys():
        if loc_id not in location_neighbours:
            continue  # No neighbours.

        for neighbour_id in location_neighbours[loc_id]:
            disowned = False
            if neighbour_id not in location_neighbours:
                disowned = True
            elif loc_id not in location_neighbours[neighbour_id]:
                disowned = True

            if disowned:
                code, level_num, name, display_name = locations[loc_id]
                neighbour_code = locations[neighbour_id][0]
                neighbour_name = locations[neighbour_id][2]

                bad_locs.append(
                    {
                        "code": code,
                        "level": levelNumberToName[level_num],
                        "name": name,
                        "problem": "has been disowned by neighbor " + neighbour_name + " (" + neighbour_code + ")",
                    }
                )

    # Finally, display the potentially-bad locations to the user.

    return render_to_response(
        "editor/templates/sanity_check.html", {"bad_locs": bad_locs}, context_instance=RequestContext(request)
    )