Querying the GitHub API for repositories and organizations

By Stuart Geiger and Jamie Whitacre, made at a SciPy 2016 sprint. See the rendered, interactive, embedable map here.

A Warning: When logged in, you can push, delete, comment, etc. using the API

The Github API is powerful. Almost anything you can do on Github can be done through the API. While this notebook is only taking you through the more passive functions that read data from Github, there are also many functions that let you make changes to Github. Be careful if you are trying out a new function!

Getting started with the Github API

We are using the githubPy library, and you are going to want to log in for much higher rate limits. You can put your username and password directly into a notebook (not recommended!) or put it in a file named "ghlogin.py" and then import it. Make sure that your ghlogin.py file is ignored by git in your .gitignore file.

Packages

We are using pygithub, geopy, and ipywidgets in this notebook. We are also using datetime, but that comes with python.


In [1]:
!pip install pygithub
!pip install geopy
!pip install ipywidgets


Requirement already satisfied (use --upgrade to upgrade): pygithub in /home/mam/anaconda3/lib/python3.5/site-packages
Requirement already satisfied (use --upgrade to upgrade): geopy in /home/mam/anaconda3/lib/python3.5/site-packages
Requirement already satisfied (use --upgrade to upgrade): ipywidgets in /home/mam/anaconda3/lib/python3.5/site-packages
Requirement already satisfied (use --upgrade to upgrade): widgetsnbextension>=1.2.6 in /home/mam/anaconda3/lib/python3.5/site-packages (from ipywidgets)
Requirement already satisfied (use --upgrade to upgrade): ipykernel>=4.2.2 in /home/mam/anaconda3/lib/python3.5/site-packages (from ipywidgets)
Requirement already satisfied (use --upgrade to upgrade): traitlets>=4.2.1 in /home/mam/anaconda3/lib/python3.5/site-packages (from ipywidgets)
Requirement already satisfied (use --upgrade to upgrade): ipython>=4.0.0 in /home/mam/anaconda3/lib/python3.5/site-packages (from ipywidgets)
Requirement already satisfied (use --upgrade to upgrade): notebook>=4.2.0 in /home/mam/notebook (from widgetsnbextension>=1.2.6->ipywidgets)
Requirement already satisfied (use --upgrade to upgrade): jinja2 in /home/mam/anaconda3/lib/python3.5/site-packages (from notebook>=4.2.0->widgetsnbextension>=1.2.6->ipywidgets)
Requirement already satisfied (use --upgrade to upgrade): tornado>=4 in /home/mam/anaconda3/lib/python3.5/site-packages (from notebook>=4.2.0->widgetsnbextension>=1.2.6->ipywidgets)
Requirement already satisfied (use --upgrade to upgrade): ipython_genutils in /home/mam/anaconda3/lib/python3.5/site-packages (from notebook>=4.2.0->widgetsnbextension>=1.2.6->ipywidgets)
Requirement already satisfied (use --upgrade to upgrade): jupyter_core in /home/mam/anaconda3/lib/python3.5/site-packages (from notebook>=4.2.0->widgetsnbextension>=1.2.6->ipywidgets)
Requirement already satisfied (use --upgrade to upgrade): jupyter_client in /home/mam/anaconda3/lib/python3.5/site-packages (from notebook>=4.2.0->widgetsnbextension>=1.2.6->ipywidgets)
Requirement already satisfied (use --upgrade to upgrade): nbformat in /home/mam/anaconda3/lib/python3.5/site-packages (from notebook>=4.2.0->widgetsnbextension>=1.2.6->ipywidgets)
Requirement already satisfied (use --upgrade to upgrade): nbconvert in /home/mam/anaconda3/lib/python3.5/site-packages (from notebook>=4.2.0->widgetsnbextension>=1.2.6->ipywidgets)
Requirement already satisfied (use --upgrade to upgrade): terminado>=0.3.3 in /home/mam/anaconda3/lib/python3.5/site-packages (from notebook>=4.2.0->widgetsnbextension>=1.2.6->ipywidgets)
Requirement already satisfied (use --upgrade to upgrade): MarkupSafe in /home/mam/anaconda3/lib/python3.5/site-packages (from jinja2->notebook>=4.2.0->widgetsnbextension>=1.2.6->ipywidgets)

In [2]:
from github import Github

In [3]:
#this is my private login credentials, stored in ghlogin.py
import ghlogin

In [4]:
g = Github(login_or_token=ghlogin.gh_user, password=ghlogin.gh_passwd)

With this Github object, you can retreive all kinds of Github objects, which you can then futher explore.

Exploring methods and properties of objects.

A quick lightning tutorial inside this tutorial: there are many ways to explore the properties and methods of various objects. This is very useful when exploring a new method.

One way is to use tab completion, which is supported in Jupyter notebooks. Once you have executed code storing an object to a variable, type the variable name, then a dot, then hit tab to explore. If you don't have this, you can also use an extended version of the dir function. This vdir() function shows the methods and properties of an object, excluding those that begin with underscores (which are ones you will likely not use in this tutorial).


In [ ]:


In [43]:
def vdir(obj):
    return [x for x in dir(obj) if not x.startswith('_')]

In [44]:
vdir(g)


Out[44]:
['FIX_REPO_GET_GIT_REF',
 'create_from_raw_data',
 'dump',
 'get_api_status',
 'get_api_status_messages',
 'get_emojis',
 'get_gist',
 'get_gists',
 'get_gitignore_template',
 'get_gitignore_templates',
 'get_hook',
 'get_hooks',
 'get_last_api_status_message',
 'get_organization',
 'get_rate_limit',
 'get_repo',
 'get_repos',
 'get_user',
 'get_users',
 'legacy_search_repos',
 'legacy_search_user_by_email',
 'legacy_search_users',
 'load',
 'oauth_scopes',
 'per_page',
 'rate_limiting',
 'rate_limiting_resettime',
 'render_markdown',
 'search_code',
 'search_issues',
 'search_repositories',
 'search_users']

Users

To get a user object, call the get_user() function of the main Github object.


In [45]:
user = g.get_user("staeiou")

In [47]:
vdir(user)


Out[47]:
['CHECK_AFTER_INIT_FLAG',
 'avatar_url',
 'bio',
 'blog',
 'collaborators',
 'company',
 'contributions',
 'created_at',
 'disk_usage',
 'email',
 'etag',
 'events_url',
 'followers',
 'followers_url',
 'following',
 'following_url',
 'get_events',
 'get_followers',
 'get_following',
 'get_gists',
 'get_keys',
 'get_orgs',
 'get_public_events',
 'get_public_received_events',
 'get_received_events',
 'get_repo',
 'get_repos',
 'get_starred',
 'get_subscriptions',
 'get_watched',
 'gists_url',
 'gravatar_id',
 'has_in_following',
 'hireable',
 'html_url',
 'id',
 'last_modified',
 'location',
 'login',
 'name',
 'organizations_url',
 'owned_private_repos',
 'plan',
 'private_gists',
 'public_gists',
 'public_repos',
 'raw_data',
 'raw_headers',
 'received_events_url',
 'repos_url',
 'setCheckAfterInitFlag',
 'starred_url',
 'subscriptions_url',
 'total_private_repos',
 'type',
 'update',
 'updated_at',
 'url']

In [46]:
print(user.name)
print(user.created_at)
print(user.location)


Stuart Geiger
2013-06-14 00:25:39
Berkeley, CA

Repositories

Repositories work similarly to users. You have to call the name of the user or organization that owns the repository, then a slash, then the name of the repository. Some of these objects are easily printed (like name, description), while others are fully fledged Github objects in themselves, with many methods and properties (like organization or commit)


In [7]:
repo = g.get_repo("jupyter/notebook")

In [48]:
vdir(repo)


Out[48]:
['CHECK_AFTER_INIT_FLAG',
 'add_to_collaborators',
 'archive_url',
 'assignees_url',
 'blobs_url',
 'branches_url',
 'clone_url',
 'collaborators_url',
 'comments_url',
 'commits_url',
 'compare',
 'compare_url',
 'contents_url',
 'contributors_url',
 'create_git_blob',
 'create_git_commit',
 'create_git_ref',
 'create_git_release',
 'create_git_tag',
 'create_git_tag_and_release',
 'create_git_tree',
 'create_hook',
 'create_issue',
 'create_key',
 'create_label',
 'create_milestone',
 'create_pull',
 'created_at',
 'default_branch',
 'delete',
 'description',
 'downloads_url',
 'edit',
 'etag',
 'events_url',
 'fork',
 'forks',
 'forks_count',
 'forks_url',
 'full_name',
 'get_archive_link',
 'get_assignees',
 'get_branch',
 'get_branches',
 'get_collaborators',
 'get_comment',
 'get_comments',
 'get_commit',
 'get_commits',
 'get_contents',
 'get_contributors',
 'get_dir_contents',
 'get_download',
 'get_downloads',
 'get_events',
 'get_file_contents',
 'get_forks',
 'get_git_blob',
 'get_git_commit',
 'get_git_ref',
 'get_git_refs',
 'get_git_tag',
 'get_git_tree',
 'get_hook',
 'get_hooks',
 'get_issue',
 'get_issues',
 'get_issues_comments',
 'get_issues_event',
 'get_issues_events',
 'get_key',
 'get_keys',
 'get_label',
 'get_labels',
 'get_languages',
 'get_milestone',
 'get_milestones',
 'get_network_events',
 'get_pull',
 'get_pulls',
 'get_pulls_comments',
 'get_pulls_review_comments',
 'get_readme',
 'get_release',
 'get_releases',
 'get_stargazers',
 'get_stargazers_with_dates',
 'get_stats_code_frequency',
 'get_stats_commit_activity',
 'get_stats_contributors',
 'get_stats_participation',
 'get_stats_punch_card',
 'get_subscribers',
 'get_tags',
 'get_teams',
 'get_watchers',
 'git_commits_url',
 'git_refs_url',
 'git_tags_url',
 'git_url',
 'has_downloads',
 'has_in_assignees',
 'has_in_collaborators',
 'has_issues',
 'has_wiki',
 'homepage',
 'hooks_url',
 'html_url',
 'id',
 'issue_comment_url',
 'issue_events_url',
 'issues_url',
 'keys_url',
 'labels_url',
 'language',
 'languages_url',
 'last_modified',
 'legacy_search_issues',
 'master_branch',
 'merge',
 'merges_url',
 'milestones_url',
 'mirror_url',
 'name',
 'network_count',
 'notifications_url',
 'open_issues',
 'open_issues_count',
 'organization',
 'owner',
 'parent',
 'permissions',
 'private',
 'pulls_url',
 'pushed_at',
 'raw_data',
 'raw_headers',
 'remove_from_collaborators',
 'setCheckAfterInitFlag',
 'size',
 'source',
 'ssh_url',
 'stargazers_count',
 'stargazers_url',
 'statuses_url',
 'subscribe_to_hub',
 'subscribers_url',
 'subscription_url',
 'svn_url',
 'tags_url',
 'teams_url',
 'trees_url',
 'unsubscribe_from_hub',
 'update',
 'updated_at',
 'url',
 'watchers',
 'watchers_count']

In [ ]:

There are lots of properties or methods of objects that return other objects (like repos, users, organizations), and you can quickly access properties or methods of these objects with a dot.

There there are also methods that return lists of objects, like repo.get_commits() or repo.get_contributors(). You need to iterate through these lists, or access them with indexes. What you usually get from these lists are also objects that have their own properties and methods.


In [52]:
print(repo.name)
print(repo.description)
print(repo.organization)
print(repo.organization.name)
print(repo.organization.location)
print(repo.language)
print(repo.get_contributors())
print(repo.get_commits())


notebook
Jupyter Interactive Notebook
<github.Organization.Organization object at 0x7f2c26dd0d30>
Project Jupyter
The Future
JavaScript
<github.PaginatedList.PaginatedList object at 0x7f2c450dd7b8>
<github.PaginatedList.PaginatedList object at 0x7f2c450dd710>

Commits


In [53]:
commits = repo.get_commits()
commit = commits[0]
print("Author name: ", commit.author.name)
print("Committer name: ", commit.committer.name)
print("Lines added: ", commit.stats.additions)
print("Lines deleted: ", commit.stats.deletions)
print("Commit message:\n---------\n", commit.commit.message)


Author name:  Matthias Bussonnier
Committer name:  GitHub Web Flow
Lines added:  5
Lines deleted:  0
Commit message:
---------
 Merge pull request #1614 from staeiou/master

Add info on how to launch master branch install

Working with timedeltas

This is a function that iterates through all the commits in a repository since one month ago, and then counts the number of commits and the net lines added/removed.


In [54]:
import datetime

In [55]:
one_month_ago = datetime.datetime.now() - datetime.timedelta(days=30)
net_lines_added = 0
num_commits = 0

for commit in repo.get_commits(since = one_month_ago):
    net_lines_added += commit.stats.additions
    net_lines_added -= commit.stats.deletions
    num_commits += 1
    
print(net_lines_added, num_commits)


58 28

Issues

Issues are objects similar to commits.


In [56]:
dir(issue)


Out[56]:
['CHECK_AFTER_INIT_FLAG',
 '_CompletableGithubObject__complete',
 '_CompletableGithubObject__completed',
 '_GithubObject__makeSimpleAttribute',
 '_GithubObject__makeSimpleListAttribute',
 '_GithubObject__makeTransformedAttribute',
 '__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_assignee',
 '_body',
 '_closed_at',
 '_closed_by',
 '_comments',
 '_comments_url',
 '_completeIfNeeded',
 '_completeIfNotSet',
 '_created_at',
 '_events_url',
 '_headers',
 '_html_url',
 '_id',
 '_identity',
 '_initAttributes',
 '_labels',
 '_labels_url',
 '_makeBoolAttribute',
 '_makeClassAttribute',
 '_makeDatetimeAttribute',
 '_makeDictAttribute',
 '_makeDictOfStringsToClassesAttribute',
 '_makeIntAttribute',
 '_makeListOfClassesAttribute',
 '_makeListOfIntsAttribute',
 '_makeListOfListOfStringsAttribute',
 '_makeListOfStringsAttribute',
 '_makeStringAttribute',
 '_makeTimestampAttribute',
 '_milestone',
 '_number',
 '_parentUrl',
 '_pull_request',
 '_rawData',
 '_repository',
 '_requester',
 '_state',
 '_storeAndUseAttributes',
 '_title',
 '_updated_at',
 '_url',
 '_useAttributes',
 '_user',
 'add_to_labels',
 'assignee',
 'body',
 'closed_at',
 'closed_by',
 'comments',
 'comments_url',
 'create_comment',
 'created_at',
 'delete_labels',
 'edit',
 'etag',
 'events_url',
 'get_comment',
 'get_comments',
 'get_events',
 'get_labels',
 'html_url',
 'id',
 'labels',
 'labels_url',
 'last_modified',
 'milestone',
 'number',
 'pull_request',
 'raw_data',
 'raw_headers',
 'remove_from_labels',
 'repository',
 'setCheckAfterInitFlag',
 'set_labels',
 'state',
 'title',
 'update',
 'updated_at',
 'url',
 'user']

In [57]:
issues = repo.get_issues()
for issue in issues:
    last_updated_delta = datetime.datetime.now() - issue.updated_at
    if last_updated_delta > datetime.timedelta(days=365):
        print(issue.title, last_updated_delta.days)


Getting rid of IPython global. 391
Semantic highlighting.  407
Is it possible to add notebook metadata, like a description which shows up in the notebook file view? 407

Organizations

Organizations are objects too, which have similar properties:


In [58]:
org = g.get_organization("jupyter")

In [59]:
print(org.name)
print(org.created_at)
print(org.html_url)


Project Jupyter
2014-04-23 21:36:43
https://github.com/jupyter

We can go through all the repositories in the organization with the get_repos() function. It returns a list of repository objects, which have their own properties and methods.

In this example, we are iterating through all the repositories in an organization, then for an empty dictionary, setting the key to the repository's name and the value to the number of times the repository has been forked.


In [75]:
repos = {}

for repo in org.get_repos():
    repos[repo.name] = repo.forks_count
repos


Out[75]:
{'atom-notebook': 11,
 'cdn.jupyter.org': 2,
 'colaboratory': 43,
 'design': 13,
 'docker-demo-images': 59,
 'docker-stacks': 235,
 'enhancement-proposals': 18,
 'experiments': 0,
 'governance': 13,
 'help': 10,
 'jupyter': 90,
 'jupyter-blog-theme': 3,
 'jupyter-drive': 27,
 'jupyter-js-services': 26,
 'jupyter-js-utils': 3,
 'jupyter-sphinx-theme': 3,
 'jupyter-sprints': 1,
 'jupyter.github.io': 27,
 'jupyter_client': 67,
 'jupyter_console': 38,
 'jupyter_core': 48,
 'jupyter_kernel_test': 8,
 'jupyter_logger': 2,
 'jupyterhub-2016-workshop': 8,
 'jupyterlab': 85,
 'kernel_gateway': 13,
 'kernel_gateway_demos': 12,
 'lbnl-jupyterday': 1,
 'mozfest15-training': 3,
 'nature-demo': 5,
 'nbcache': 1,
 'nbconvert': 88,
 'nbconvert-examples': 30,
 'nbdime': 12,
 'nbformat': 29,
 'nbgrader': 50,
 'nbviewer': 213,
 'newsletter': 5,
 'ngcm-tutorial': 12,
 'notebook': 406,
 'qtconsole': 45,
 'roadmap': 12,
 'scipy-2015-advanced-topics': 1,
 'scipy-advanced-tutorial': 19,
 'scipy-sprint': 0,
 'strata-sv-2015-tutorial': 23,
 'surveys': 3,
 'testpath': 1,
 'tmpnb': 82,
 'tmpnb-deploy': 5,
 'tmpnb-redirector': 6,
 'try.jupyter.org': 2,
 'widget-cookiecutter': 4}

Getting location data for an organization's contributors

Mapping and geolocation

Before we get into how to query GitHub, we know we will have to get location coordinates for each contributor, and then plot it on a map. So we are going to do that first.

For geolocation, we are using geopy's geolocator object, which is based on Open Street Map's Nominatim API. Nominatim takes in any arbitrary location data and then returns a location object, which includes the best latitude and longitude coordinates it can find.

This does mean that we will have more error than if we did this manually, and there might be vastly different levels of accuracy. For example, if someone just has "UK" as their location, it will show up in the geographic center of the UK, which is somewhere on the edge of the Lake District. "USA" resolves to somewhere in Kansas. However, you can get very specific location data if you put in more detail.


In [24]:
from geopy.geocoders import Nominatim

geolocator = Nominatim()
uk_loc = geolocator.geocode("UK")
print(uk_loc.longitude,uk_loc.latitude)

us_loc = geolocator.geocode("USA")
print(us_loc.longitude,us_loc.latitude)

bids_loc = geolocator.geocode("Doe Library, Berkeley CA, 94720 USA")
print(bids_loc.longitude,bids_loc.latitude)


-3.2765752 54.7023545
-100.4458824 39.7837304
-122.259492086406 37.87219435

We can plot points on a map using ipyleaflets and ipywidgets. We first set up a map object, which is created with various parameters. Then we create Marker objects, which are then appended to the map. We then display the map inline in this notebook.


In [25]:
import ipywidgets

from ipyleaflet import (
    Map,
    Marker,
    TileLayer, ImageOverlay,
    Polyline, Polygon, Rectangle, Circle, CircleMarker,
    GeoJSON,
    DrawControl
)

center = [30.0, 5.0]
zoom = 2
m = Map(default_tiles=TileLayer(opacity=1.0), center=center, zoom=zoom, layout=ipywidgets.Layout(height="600px"))

uk_mark = Marker(location=[uk_loc.latitude,uk_loc.longitude])
uk_mark.visible
m += uk_mark

us_mark = Marker(location=[us_loc.latitude,us_loc.longitude])
us_mark.visible
m += us_mark

bids_mark = Marker(location=[bids_loc.latitude,bids_loc.longitude])
bids_mark.visible
m += bids_mark

In [ ]:

Rate limiting

Now that we have made a few requests, we can see what our rate limit is. If you are logged in, you get 5,000 requests per hour. If you are not, you only get 60 per hour. You can use methods in the GitHub object to see your remaining queries, hourly limit, and reset time. We have used less than 100 of our 5,000 requests with these calls.


In [20]:
g.rate_limiting


Out[20]:
(4908, 5000)

In [21]:
reset_time = g.rate_limiting_resettime
reset_time


Out[21]:
1469149774

This value is in seconds since the UTC epoch (Jan 1st, 1970), so we have to convert it. Here is a quick function that takes a GitHub object, queries the API to find our next reset time, and converts it to minutes.


In [22]:
import datetime
def minutes_to_reset(github):
    reset_time = github.rate_limiting_resettime
    timedelta_to_reset = datetime.datetime.fromtimestamp(reset_time) - datetime.datetime.now()
    return timedelta_to_reset.seconds / 60

In [23]:
minutes_to_reset(g)


Out[23]:
58.11666666666667

Querying GitHub for location data

For our mapping script, we want to get profiles for everyone who has made a commit to any of the repositories in the Jupyter organization, find their location (if any), then add it to a list. The API has a get_contributors function for repo objects, which returns a list of contributors ordered by number of commits, but not one that works across all repos in an org. So we have to iterate through all the repos in the org, and run the get_contributors method for We also want to make sure we don't add any duplicates to our list to over-represent any areas, so we keep track of people in a dictionary.

I've written a few functions to make it easy to retreive and map an organization's contributors.


In [26]:
def get_org_contributor_locations(github, org_name):
    """
    For a GitHub organization, get location for contributors to any repo in the org.
    
    Returns a dictionary of {username URLS : geopy Locations}, then a dictionary of various metadata.
    
    """
    
    # Set up empty dictionaries and metadata variables
    contributor_locs = {}
    locations = []
    none_count = 0
    error_count = 0
    user_loc_count = 0
    duplicate_count = 0
    geolocator = Nominatim()

    
    # For each repo in the organization
    for repo in github.get_organization(org_name).get_repos():
        #print(repo.name)
        
        # For each contributor in the repo        
        for contributor in repo.get_contributors():
            print('.', end="")
            # If the contributor_locs dictionary doesn't have an entry for this user
            if contributor_locs.get(contributor.url) is None:
                
                # Try-Except block to handle API errors
                try:
                    # If the contributor has no location in profile
                    if(contributor.location is None):
                        #print("No Location")
                        none_count += 1
                    else:
                        # Get coordinates for location string from Nominatim API
                        location=geolocator.geocode(contributor.location)

                        #print(contributor.location, " | ", location)
                        
                        # Add a new entry to the dictionary. Value is user's URL, key is geocoded location object
                        contributor_locs[contributor.url] = location
                        user_loc_count += 1
                except Exception:
                    print('!', end="")
                    error_count += 1
            else:
                duplicate_count += 1
                
    return contributor_locs,{'no_loc_count':none_count, 'user_loc_count':user_loc_count, 
                             'duplicate_count':duplicate_count, 'error_count':error_count}

With this, we can easily query an organization. The U.S. Digital Service (org name: usds) is a small organization that works well for testing these kinds of queries. It takes about a second per contributor to get this data, so we want to test on small orgs first. To show the status, it prints a period for each successful query and an exclaimation point for each error.

The get_org_contributor_locations function takes a Github object and an organization name, and returns two dictionaries: one of user and location data, and one of metadata about the geolocation query (including the number of users without a location in their profile).


In [27]:
usds_locs, usds_metadata = get_org_contributor_locations(g,'usds')


...............................

In [28]:
usds_metadata


Out[28]:
{'duplicate_count': 1,
 'error_count': 0,
 'no_loc_count': 8,
 'user_loc_count': 22}

We are going to explore this dataset, but not plot names or usernames. I'm a bit hesitant to publish location data with unique identifiers, even if people put that information in their profiles. This code iterates through the dictionary and puts location data into a list.


In [29]:
usds_locs_nousernames = []
for contributor, location in usds_locs.items():
    usds_locs_nousernames.append(location)
usds_locs_nousernames


Out[29]:
[Location(D,C, Buccaneer Ridge Drive, Johnson City, Washington County, Tennessee, 37614, United States of America, (36.29885175, -82.3591932141095, 0.0)),
 Location(Washington, District of Columbia, United States of America, (38.8949549, -77.0366455, 0.0)),
 Location(東京都, 日本, (34.2255804, 139.294774527387, 0.0)),
 Location(Seattle, King County, Washington, United States of America, (47.6038321, -122.3300623, 0.0)),
 Location(Washington, District of Columbia, United States of America, (38.8949549, -77.0366455, 0.0)),
 Location(Washington, District of Columbia, United States of America, (38.8949549, -77.0366455, 0.0)),
 Location(Washington, District of Columbia, United States of America, (38.8949549, -77.0366455, 0.0)),
 Location(Dayton, Montgomery County, Ohio, United States of America, (39.7589478, -84.1916068, 0.0)),
 Location(United States of America, (39.7837304, -100.4458824, 0.0)),
 Location(D,C, Buccaneer Ridge Drive, Johnson City, Washington County, Tennessee, 37614, United States of America, (36.29885175, -82.3591932141095, 0.0)),
 Location(Alexandria, Alexandria City, Virginia, United States of America, (38.8051095, -77.0470228, 0.0)),
 Location(Washington, District of Columbia, United States of America, (38.8949549, -77.0366455, 0.0)),
 Location(Brooklyn Basin, Oakland, Alameda County, California, United States of America, (37.7857615, -122.24858, 0.0)),
 Location(SF, California, United States of America, (37.7792808, -122.4192362, 0.0)),
 Location(Oakland, Alameda County, California, United States of America, (37.8044557, -122.2713562, 0.0)),
 Location(Milwaukee, Milwaukee County, Wisconsin, United States of America, (43.0349931, -87.9224969, 0.0)),
 Location(SF, California, United States of America, (37.7792808, -122.4192362, 0.0)),
 Location(SF, California, United States of America, (37.7792808, -122.4192362, 0.0)),
 Location(Port Angeles, Clallam County, Washington, United States of America, (48.118146, -123.4307412, 0.0)),
 Location(Falls Church, Falls Church City, Virginia, United States of America, (38.882334, -77.1710913, 0.0)),
 Location(Portland, Multnomah County, Oregon, United States of America, (45.5202471, -122.6741948, 0.0)),
 Location(Washington, District of Columbia, United States of America, (38.8949549, -77.0366455, 0.0))]

Now we can map this data using another function I have written.


In [81]:
def map_location_dict(map_obj,org_location_dict):
    """
    Maps the locations in a dictionary of {ids : geoPy Locations}. 
    
    Must be passed a map object, then the dictionary. Returns the map object.
    
    """
    for username, location in org_location_dict.items():
        if(location is not None):
            mark = Marker(location=[location.latitude,location.longitude])
            mark.visible
            map_obj += mark
            

    return map_obj

In [ ]:


In [82]:
center = [30.0,5.0]
zoom = 2
usds_map = Map(default_tiles=TileLayer(opacity=1.0), center=center, zoom=zoom, layout=ipywidgets.Layout(height="600px"))

usds_map = map_location_dict(usds_map, usds_locs)

Now show the map inline! With the leaflet widget, you can zoom in and out directly in the notebook. And we can also export it to an html widget by going to the Widgets menu in Jupyter notebooks, clicking "Embed widgets," and copy/pasting this to an html file. It will not show up in rendered Jupyter notebooks on Github, but may show up in nbviewer.


In [ ]:
usds_map

In [ ]: