├── .gitignore ├── README.md ├── client.py ├── requirements.txt └── templates ├── base.html ├── index.html └── receive_code.html /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | local/ 3 | include/ 4 | lib/ 5 | *.Python 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | api-example-flask 2 | ================= 3 | 4 | An example Python client application of 23andMe's API using the [flask](http://flask.pocoo.org/) microframework. 5 | 6 | Clone the repository, create a virtual environment in the directory, and start using it: 7 | ``` 8 | cd api-example-flask && virtualenv . 9 | source bin/activate 10 | ``` 11 | 12 | Then, install the required packages (```flask``` and ```requests```): 13 | ``` 14 | pip install -r requirements.txt 15 | ``` 16 | 17 | You're good to go. Start it with: 18 | ``` 19 | python client.py 20 | ``` 21 | -------------------------------------------------------------------------------- /client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import getpass 4 | import requests 5 | import flask 6 | from flask import request 7 | import os 8 | from optparse import OptionParser 9 | from requests_oauthlib import OAuth2Session 10 | 11 | PORT = 5000 12 | API_SERVER = 'api.23andme.com' 13 | BASE_CLIENT_URL = 'http://localhost:%s/' % PORT 14 | DEFAULT_REDIRECT_URI = '%sreceive_code/' % BASE_CLIENT_URL 15 | 16 | # so we don't get errors if the redirect uri is not https 17 | os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = '1' 18 | 19 | # 20 | # you can pass in more scopes through the command line, or change these 21 | # 22 | DEFAULT_SNPS = ['rs12913832'] 23 | DEFAULT_SCOPES = ['names', 'basic'] + DEFAULT_SNPS 24 | 25 | # 26 | # the command line will ask for a client_secret if you choose to not hardcode the app's client_secret here 27 | # 28 | client_secret = None 29 | 30 | parser = OptionParser(usage="usage: %prog -i CLIENT_ID [options]") 31 | parser.add_option("-i", "--client-id", dest="client_id", default='', 32 | help="Your client_id [REQUIRED]") 33 | parser.add_option('-s', '--scopes', dest='scopes', action='append', default=[], 34 | help='Your requested scopes. Eg: -s basic -s rs12913832') 35 | parser.add_option("-r", "--redirect_uri", dest="redirect_uri", default=DEFAULT_REDIRECT_URI, 36 | help="Your client's redirect_uri [%s]" % DEFAULT_REDIRECT_URI) 37 | parser.add_option("-a", "--api_server", dest="api_server", default=API_SERVER, 38 | help="Almost always: [api.23andme.com]") 39 | parser.add_option("-p", "--select-profile", dest='select_profile', action='store_true', default=False, 40 | help='If present, the auth screen will show a profile select screen') 41 | 42 | (options, args) = parser.parse_args() 43 | BASE_API_URL = "https://%s" % options.api_server 44 | API_AUTH_URL = '%s/authorize' % BASE_API_URL 45 | API_TOKEN_URL = '%s/token/' % BASE_API_URL 46 | 47 | if options.select_profile: 48 | API_AUTH_URL += '?select_profile=true' 49 | 50 | redirect_uri = options.redirect_uri 51 | client_id = options.client_id 52 | 53 | scopes = options.scopes or DEFAULT_SCOPES 54 | 55 | if not options.client_id: 56 | print "missing param: CLIENT_ID:" 57 | parser.print_usage() 58 | print "Please navigate to your developer dashboard [%s/dev/] to retrieve your client_id.\n" % BASE_API_URL 59 | exit() 60 | 61 | if not client_secret: 62 | print "Please navigate to your developer dashboard [%s/dev/] to retrieve your client_secret." % BASE_API_URL 63 | client_secret = getpass.getpass("Please enter your client_secret:") 64 | 65 | app = flask.Flask(__name__) 66 | 67 | 68 | @app.route('/') 69 | def index(): 70 | ttam_oauth = OAuth2Session(client_id, 71 | redirect_uri=redirect_uri, 72 | scope=scopes) 73 | auth_url, state = ttam_oauth.authorization_url(API_AUTH_URL) 74 | return flask.render_template('index.html', auth_url=auth_url) 75 | 76 | 77 | @app.route('/receive_code/') 78 | def receive_code(): 79 | # 80 | # now we hit the /token endpoint to get the access_token 81 | # 82 | ttam_oauth = OAuth2Session(client_id, 83 | redirect_uri=redirect_uri) 84 | token_dict = ttam_oauth.fetch_token(API_TOKEN_URL, 85 | client_secret=client_secret, 86 | authorization_response=request.url) 87 | 88 | # 89 | # the response token_dict is of the form 90 | # { 91 | # 'token_type': 'bearer', 92 | # 'refresh_token': '7cb92495fe515f0bfe94775e2b06b46b', 93 | # 'access_token': 'ad7ace51ad19732b3f9ef778dc766fce', 94 | # 'scope': ['rs12913832', 'names', 'basic'], 95 | # 'expires_in': 86400, 'expires_at': 1475283697.571757 96 | # } 97 | # 98 | 99 | access_token = token_dict['access_token'] 100 | 101 | # 102 | # now you have the access_token to make queries with 103 | # enter your code here 104 | # 105 | # the following is a sample of how you can use the access_token to make your API queries 106 | # it makes query to the /genotype endpoint and gets information about the requested SNPS 107 | # 108 | headers = {'Authorization': 'Bearer %s' % access_token} 109 | genotype_response = requests.get("%s%s" % (BASE_API_URL, "/1/genotype/"), 110 | params={'locations': ' '.join(DEFAULT_SNPS)}, 111 | headers=headers, 112 | verify=False) 113 | if genotype_response.status_code == 200: 114 | return flask.render_template('receive_code.html', response_json=genotype_response.json()) 115 | else: 116 | # print 'response text = ', genotype_response.text 117 | genotype_response.raise_for_status() 118 | 119 | 120 | if __name__ == '__main__': 121 | print "A local client for the Personal Genome API is now initialized." 122 | app.run(debug=False, port=PORT) 123 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.20.0 2 | flask==1.0 3 | requests-oauthlib==0.6.2 -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |This demo will take you through the basic authentication flow
9 |Click here to authorize this client to access your genetics. After authorizing the application, you will be forwarded back to this application with a code that displays
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /templates/receive_code.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block body %} 3 |You've retrieved the following genotypes from the api
5 |profile id | 8 |rs12913832 | 9 |
---|---|
{{p.id}} | 13 |{{p.rs12913832}} | 14 |