From 3dbbe5a03157cf33140668fcac521d44a87615dc Mon Sep 17 00:00:00 2001 From: Doug Henderson Date: Sat, 21 May 2016 02:35:28 -0600 Subject: [PATCH 01/14] update tests to work with both Python 2.7 and 3.5 --- test/exampleconfig.ini | 2 + test/run_tests.py | 9 +- test/test_basefs.py | 4 +- test/test_enhanced_request.py | 180 ++++++++++++++++++++++++---------- test/util.py | 9 +- 5 files changed, 144 insertions(+), 60 deletions(-) diff --git a/test/exampleconfig.ini b/test/exampleconfig.ini index c3f45d6..7feac44 100644 --- a/test/exampleconfig.ini +++ b/test/exampleconfig.ini @@ -1,4 +1,6 @@ +# Copy this file to config.ini and edit it. # Replace the values for user, password and devkey with your own valid values. +# As per API rules, I cannot provide one. [fsTest] user=myusername password=mypassword diff --git a/test/run_tests.py b/test/run_tests.py index d9bc157..f5231ff 100644 --- a/test/run_tests.py +++ b/test/run_tests.py @@ -1,9 +1,12 @@ +# -*- coding: utf-8 -*- + +from __future__ import print_function from test.test_basefs import TestFamilySearch -#from test.test_enhanced_request import TestEnhancedRequest +from test.test_enhanced_request import TestEnhancedRequest fs = TestFamilySearch() fs.runTest() # Removing until we can get it platform-independent... -#er = TestEnhancedRequest() -#er.runTest() +er = TestEnhancedRequest() +er.runTest() diff --git a/test/test_basefs.py b/test/test_basefs.py index 06286bf..f56d6c5 100644 --- a/test/test_basefs.py +++ b/test/test_basefs.py @@ -3,6 +3,7 @@ Test the base familysearch module contained in __init__.py """ # import system modules +from __future__ import print_function import json try: from urllib import request @@ -17,7 +18,7 @@ class TestFamilySearch(util.FSTemplateTest): """Test the base familysearch module contained in __init__.py""" - + def runTest(self): self.setUp() self.test_base_fs_creation() @@ -42,4 +43,3 @@ def test_base_fs_creation(self): print("HTTP opener works.") self.assertTrue(fs.access_token == None) print("Access token works.") - diff --git a/test/test_enhanced_request.py b/test/test_enhanced_request.py index 54b9311..fbf565c 100644 --- a/test/test_enhanced_request.py +++ b/test/test_enhanced_request.py @@ -3,7 +3,9 @@ Test the EnhancedRequest object contained in __init__.py """ # import system modules +from __future__ import print_function, unicode_literals import json +import pprint # Python imports try: # Python 3 @@ -26,6 +28,17 @@ class TestEnhancedRequest(util.FSTemplateTest): """Test the EnhancedRequest object contained in __init__.py""" + + def runTest(self): + self.setUp() + self.test_delete() + self.test_get() + self.test_head() + self.test_options() + self.test_post() + self.test_put() + self.tearDown() + # setup def setUp(self): util.FSTemplateTest.setUp(self) @@ -37,29 +50,62 @@ def tearDown(self): util.FSTemplateTest.tearDown(self) + @staticmethod + def _hr(): + print("="*80) + + def _show_in_out(self, actual, expected, msg_actual, msg_expected): + + pp = pprint.PrettyPrinter(indent=2, width=120) + + if isinstance(expected, dict): + print(msg_actual, type(actual), "...") + pp.pprint(actual) + print(msg_expected, type(expected), "...") + pp.pprint(expected) + + elif isinstance(expected, list): + print(msg_actual, type(actual), "...") + pp.pprint(actual) + print(msg_expected, type(expected), "...") + pp.pprint(expected) + else: + print("actual:", type(actual), "expected:", type(expected)) + raise TypeError + + self._hr() + def test_delete(self): expected = { 'args': {}, 'form': {}, - 'origin': '192.168.1.1', + 'origin': '192.168.1.70', 'headers': { - 'X-Request-Id': 'e4f0cb78-afc7-4617-aac1-a0c13fa746cc', + # 'X-Request-Id': 'e4f0cb78-afc7-4617-aac1-a0c13fa746cc', 'Accept-Encoding': 'identity', - 'User-Agent': 'Python-urllib/3.4', + 'User-Agent': 'Python-urllib/3.5', 'Host': 'httpbin.org', - 'Connection': 'close' + #'Connection': 'close', }, 'data': '', 'files': {}, 'url': 'http://httpbin.org/delete', - 'json': None + 'json': None, } url = self.base_url + '/delete' request = Request(url, method='DELETE') response = urlopen(request) actual = json.loads(response.read().decode('utf-8')) self.assertTrue(actual) - self.assertTrue(actual.keys() == expected.keys()) + actual_keys = list(actual.keys()) + actual_keys.sort() + expected_keys = list(expected.keys()) + expected_keys.sort() + # self._hr() + # self._show_in_out(actual_keys, expected_keys, "actual keys", "expected keys") + self.assertTrue(actual_keys == expected_keys) + # self._hr() + # self._show_in_out(actual.get('headers', {}).keys(), expected['headers'].keys(), "actual[headers]", "expected[headers]") self.assertTrue(actual.get('headers', {}).keys() == expected['headers'].keys()) @@ -67,13 +113,13 @@ def test_delete(self): def test_get(self): expected = { 'args': {}, - 'origin': '192.168.1.1', + 'origin': '192.168.1.70', 'headers': { - 'X-Request-Id': 'e4f0cb78-afc7-4617-aac1-a0c13fa746cc', + #'X-Request-Id': 'e4f0cb78-afc7-4617-aac1-a0c13fa746cc', 'Accept-Encoding': 'identity', - 'User-Agent': 'Python-urllib/3.4', + 'User-Agent': 'Python-urllib/3.5', 'Host': 'httpbin.org', - 'Connection': 'close' + #'Connection': 'close', }, 'url': 'http://httpbin.org/get', } @@ -89,65 +135,76 @@ def test_get(self): def test_head(self): expected = [ - ('Access-Control-Allow-Credentials', 'true'), - ('Access-Control-Allow-Origin', '*'), - ('Content-length', '294'), - ('Content-Type', 'application/json'), - ('Date', 'Thu, 18 Sep 2014 00:29:57 GMT'), - ('Server', 'gunicorn/18.0'), - ('Connection', 'Close') + ('access-control-allow-credentials', 'true'), + ('access-control-allow-origin', '*'), + ('content-length', '294'), + ('content-type', 'application/json'), + ('date', 'Thu, 18 Sep 2014 00:29:57 GMT'), + ('server', 'gunicorn/18.0'), + ('connection', 'Close') ] url = self.base_url + '/get' request = Request(url, method='HEAD') response = urlopen(request) - actual = response.getheaders() - expected_fields = [value[0] for value in expected] - actual_fields = [value[0] for value in actual] + #print("dir(response):", dir(response)) + #print("headers:", type(response.headers), response.headers.__class__) + #print(response.headers.__class__, dir(response.headers)) + actual = response.headers self.assertTrue(actual) - self.assertTrue(actual_fields == expected_fields) + expected_keys = [k for k, v in expected] + actual_keys = [k.lower() for k in actual.keys()] + expected_keys.sort() + actual_keys.sort() + #self._hr() + #self._show_in_out(actual_keys, expected_keys, "actual keys", "expected keys") + self.assertTrue(actual_keys == expected_keys) def test_options(self): expected = [ - ('Access-Control-Allow-Credentials', 'true'), - ('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS'), - ('Access-Control-Allow-Origin', '*'), - ('Access-Control-Max-Age', '3600'), - ('Allow', 'GET, HEAD, OPTIONS'), - ('Content-Type', 'text/html; charset=utf-8'), - ('Date', 'Thu, 18 Sep 2014 00:29:57 GMT'), - ('Server', 'gunicorn/18.0'), - ('Content-Length', '0'), - ('Connection', 'Close') + ('access-control-allow-credentials', 'true'), + ('access-control-allow-methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS'), + ('access-control-allow-origin', '*'), + ('access-control-max-age', '3600'), + ('allow', 'GET, HEAD, OPTIONS'), + ('content-type', 'text/html; charset=utf-8'), + ('date', 'Thu, 18 Sep 2014 00:29:57 GMT'), + ('server', 'gunicorn/18.0'), + ('content-length', '0'), + ('connection', 'Close') ] url = self.base_url request = Request(url, method='OPTIONS') response = urlopen(request) - actual = response.getheaders() - expected_fields = [value[0] for value in expected] - actual_fields = [value[0] for value in actual] + actual = response.headers self.assertTrue(actual) - self.assertTrue(actual_fields == expected_fields) + expected_keys = [k for k, v in expected] + actual_keys = [k.lower() for k in actual.keys()] + expected_keys.sort() + actual_keys.sort() + #self._hr() + #self._show_in_out(actual_keys, expected_keys, "actual keys", "expected keys") + self.assertTrue(actual_keys == expected_keys) def test_post(self): expected = { 'args': {}, 'form': {'spam': '1', 'eggs': '2', 'bacon': '3'}, - 'origin': '192.168.1.1', + 'origin': '192.168.1.70', 'headers': { 'Content-Length': '21', - 'X-Request-Id': 'e4f0cb78-afc7-4617-aac1-a0c13fa746cc', + #'X-Request-Id': 'e4f0cb78-afc7-4617-aac1-a0c13fa746cc', 'Accept-Encoding': 'identity', 'Content-Type': 'application/x-www-form-urlencoded', - 'User-Agent': 'Python-urllib/3.4', + 'User-Agent': 'Python-urllib/3.5', 'Host': 'httpbin.org', - 'Connection': 'close' + #'Connection': 'close', }, 'data': '', 'files': {}, 'url': 'http://httpbin.org/delete', - 'json': None + 'json': None, } url = self.base_url + '/post' data_dict = {'spam': 1, 'eggs': 2, 'bacon': 3} @@ -157,9 +214,20 @@ def test_post(self): response = urlopen(request) actual = json.loads(response.read().decode('utf-8')) self.assertTrue(actual) - self.assertTrue(actual.keys() == expected.keys()) - self.assertTrue(actual.get('headers', {}).keys() == - expected['headers'].keys()) + actual_keys = list(actual.keys()) + expected_keys = list(expected.keys()) + actual_keys.sort() + expected_keys.sort() + #self._hr() + #self._show_in_out(actual_keys, expected_keys, "actual keys", "expected keys") + self.assertTrue(actual_keys == expected_keys) + akeys = list(actual.get('headers', {}).keys()) + ekeys = list(expected['headers'].keys()) + akeys.sort() + ekeys.sort() + #self._hr() + #self._show_in_out(akeys, ekeys, "actual head keys", "expected head keys") + self.assertTrue(akeys == ekeys) self.assertTrue(actual.get('form', None) == expected['form']) @@ -167,14 +235,14 @@ def test_put(self): expected = { 'args': {}, 'form': {}, - 'origin': '192.168.1.1', + 'origin': '192.168.1.70', 'headers': { 'Content-Length': '0', - 'X-Request-Id': 'e4f0cb78-afc7-4617-aac1-a0c13fa746cc', + #'X-Request-Id': 'e4f0cb78-afc7-4617-aac1-a0c13fa746cc', 'Accept-Encoding': 'identity', - 'User-Agent': 'Python-urllib/3.4', + 'User-Agent': 'Python-urllib/3.5', 'Host': 'httpbin.org', - 'Connection': 'close' + #'Connection': 'close' }, 'data': '', 'files': {}, @@ -186,7 +254,17 @@ def test_put(self): response = urlopen(request) actual = json.loads(response.read().decode('utf-8')) self.assertTrue(actual) - self.assertTrue(actual.keys() == expected.keys()) - self.assertTrue(actual.get('headers', {}).keys() == - expected['headers'].keys()) - + akeys = list(actual.keys()) + ekeys = list(expected.keys()) + akeys.sort() + ekeys.sort() + self._hr() + self._show_in_out(akeys, ekeys, "actual keys", "expected keys") + self.assertTrue(akeys == ekeys) + akeys = list(actual["headers"].keys()) + ekeys = list(expected["headers"].keys()) + akeys.sort() + ekeys.sort() + self._hr() + self._show_in_out(akeys, ekeys, "actual head keys", "expected head keys") + self.assertTrue(akeys == ekeys) diff --git a/test/util.py b/test/util.py index 4306ad4..9baa47d 100644 --- a/test/util.py +++ b/test/util.py @@ -2,6 +2,7 @@ """Utilities to assist in testing.""" # import system modules +from __future__ import print_function import inspect import os import sys @@ -36,15 +37,15 @@ def setUp(self): unittest.TestCase.setUp(self) self.config = get_config() try: - #Python 2 + # Python 2 self.user = self.config.get('fsTest', 'user') self.password = self.config.get('fsTest', 'password') - self.devkey = self.config.get('fsTest', 'devkey') + self.devkey = self.config.get('fsTest', 'devkey') except AttributeError: # Python 3 self.devkey = self.config["fsTest"]["devkey"] - self.username = self.config["fsTest"]["username"] - self.username = self.config["fsTest"]["password"] + self.user = self.config["fsTest"]["username"] + self.password = self.config["fsTest"]["password"] # common teardown def tearDown(self): From 12fab1b4df5e1a465cc87ee3ae58e262e032c97e Mon Sep 17 00:00:00 2001 From: Doug Henderson Date: Sat, 21 May 2016 02:37:07 -0600 Subject: [PATCH 02/14] ignore all user customized config.ini files, editor backups, and build artifacts --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 446ff23..6e0a76e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ lib include man share +__pycache__ +*.egg-info # Ignore Python byte compiled files and Windows GUI Python files *.pyc @@ -20,6 +22,8 @@ share # Ignore text editor backup files *~ +*.bak -# Ignore test/config.ini +# Ignore all config.ini file: they contain private config info test/config.ini +sample-apps/*/config.ini From 2d8bf570f5991c140907aaec693c0afa62a000eb Mon Sep 17 00:00:00 2001 From: Doug Henderson Date: Sat, 21 May 2016 02:40:25 -0600 Subject: [PATCH 03/14] (attempt) to get sample apps working with current authentication methods --- sample-apps/web_person/exampleconfig.ini | 4 +- sample-apps/web_person/web-person.py | 56 +++++++++++++++++++----- sample-apps/whoami/exampleconfig.ini | 2 +- sample-apps/whoami/whoami.py | 34 ++++++++++++-- 4 files changed, 80 insertions(+), 16 deletions(-) diff --git a/sample-apps/web_person/exampleconfig.ini b/sample-apps/web_person/exampleconfig.ini index badd482..e49d2f9 100644 --- a/sample-apps/web_person/exampleconfig.ini +++ b/sample-apps/web_person/exampleconfig.ini @@ -1,10 +1,10 @@ +# Copy this file to config.ini and edit it. # Replace the value for devkey with your own dev key. # As per API rules, I cannot provide one. -# Then rename this file config.ini to run the sample apps. [fskey] devkey=MY-MAGIC-DEV-KEY-THAT-ALLOWS-FAMILY-SEARCH-ACCESS base=https://sandbox.familysearch.org [server] port=63342 -redirect_uri=http://localhost:63342/fspy \ No newline at end of file +redirect_uri=http://localhost:63342/fspy diff --git a/sample-apps/web_person/web-person.py b/sample-apps/web_person/web-person.py index a83e4f0..a4a4828 100644 --- a/sample-apps/web_person/web-person.py +++ b/sample-apps/web_person/web-person.py @@ -1,6 +1,10 @@ +# -*- coding: utf-8 -*- + +from __future__ import print_function import webbrowser import os import sys +import pprint try: # Python 3 @@ -12,7 +16,7 @@ import ConfigParser as configparser import BaseHTTPServer as server from urlparse import parse_qs - + from familysearch import FamilySearch config_path = os.path.dirname(os.path.abspath(sys.argv[0])) + "/config.ini" @@ -30,7 +34,7 @@ port = config.get("server", "port") redirect = config.get("server", "redirect_uri") -url = "http://localhost" + (":"+ port) if port is not "80" else "" +url = "http://localhost" + (":" + port) if port is not "80" else "" ruri = "" for x in redirect[::-1]: ruri = x + ruri @@ -39,8 +43,39 @@ fs = FamilySearch("FSPySDK/SampleApps", app_key, base=base) -fslogin = fs.root_collection['collections'][0]['links']\ - ['http://oauth.net/core/2.0/endpoint/authorize']['href'] +try: + fslogin = fs.root_collection["response"]['collections'][0]['links']\ + ['http://oauth.net/core/2.0/endpoint/authorize']['href'] +except KeyError as e: + print("KeyError:", str(e)) + raise + +def qshow(): + def hr(): print("="*80) + hr() + print("fs.root_collection: ...") + pp = pprint.PrettyPrinter(width=120, indent=2) + pp.pprint(fs.root_collection) + hr() + print("""fs.root_collection["response"]: ...""") + pp.pprint(fs.root_collection["response"]) + hr() + print("""fs.root_collection["response"]["collections"]: ...""") + pp.pprint(fs.root_collection["response"]["collections"]) + hr() + print("""fs.root_collection["response"]["collections"][0]: ...""") + pp.pprint(fs.root_collection["response"]["collections"][0]) + hr() + print("""fs.root_collection["response"]["collections"][0]["links"]: ...""") + pp.pprint(fs.root_collection["response"]["collections"][0]["links"]) + hr() + print("""fs.root_collection["response"]["collections"][0]["links"]['http://oauth.net/core/2.0/endpoint/authorize']: ...""") + pp.pprint(fs.root_collection["response"]["collections"][0]["links"]['http://oauth.net/core/2.0/endpoint/authorize']) + hr() + print("""fs.root_collection["response"]["collections"][0]["links"]['http://oauth.net/core/2.0/endpoint/authorize']['href']: ...""") + pp.pprint(fs.root_collection["response"]["collections"][0]["links"]['http://oauth.net/core/2.0/endpoint/authorize']['href']) + hr() +qshow() fslogin = fs._add_query_params(fslogin, {'response_type': 'code', 'client_id': fs.key, @@ -53,7 +88,7 @@ def do_GET(self): self.send_header("Content-type", "text/html") self.end_headers() path = self.path - + top = "FSPySDK Sample App" middle = "" bottom = "" @@ -71,15 +106,16 @@ def do_GET(self): middle = self.not_logged_in() body = top + middle + bottom - + self.wfile.write(body.encode("utf-8")) def not_logged_in(self): string = '' + string += '","fsWindow","width=560,height=632");}' string += "" string += "" return string + def logged_in(self): string = 'Search given FamilySearch PID (default is your own)
' string +='' if person['response']['persons'][0]['display']['gender'] == "Male": - string += 'He' - else: + string += 'He' + else: string += 'She' string += " is " if person['persons'][0]['living']: @@ -116,4 +152,4 @@ def get_code(self, path): return '' webbrowser.open(url) -server.HTTPServer(('', 63342), getter).serve_forever() \ No newline at end of file +server.HTTPServer(('', int(port)), getter).serve_forever() diff --git a/sample-apps/whoami/exampleconfig.ini b/sample-apps/whoami/exampleconfig.ini index e30af4b..743ddba 100644 --- a/sample-apps/whoami/exampleconfig.ini +++ b/sample-apps/whoami/exampleconfig.ini @@ -1,6 +1,6 @@ +# Copy this file to config.ini and edit it. # Replace the value for devkey with your own dev key. # As per API rules, I cannot provide one. -# Then rename this file config.ini to run the sample apps. [fskey] devkey=MY-MAGIC-DEV-KEY-THAT-ALLOWS-FAMILY-SEARCH-ACCESS base=https://sandbox.familysearch.org diff --git a/sample-apps/whoami/whoami.py b/sample-apps/whoami/whoami.py index cb14203..d1a7e4a 100644 --- a/sample-apps/whoami/whoami.py +++ b/sample-apps/whoami/whoami.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + # Python imports from __future__ import print_function import os @@ -23,21 +25,47 @@ config = configparser.ConfigParser() config.read(config_path) try: + # Python 3 app_key = config["fskey"]["devkey"] base = config["fskey"]["base"] + try: + user = config["fskey"]["user"] + except KeyError: + user = None + try: + password = config["fskey"]["password"] + except KeyError: + password = None except AttributeError: + # Python 2 app_key = config.get("fskey", "devkey") base = config.get("fskey", "base") + user = config.get("fskey", "user") + password = config.get("fskey", "password") # Sign into FamilySearch, and keep on trying until successful. fs = FamilySearch("FSPySDK/SampleApps", app_key, base=base) -print("Please sign in to FamilySearch.") -while fs.logged_in is not True: +if user and password: + print("Try user/password from config.ini") + try: + fs.login(user, password) + except HTTPError as e: + print("HTTPError:", str(e)) + #print("dir(e):", dir(e)) + print("code:", e.code) + print("hdrs:", str(e.hdrs).replace("\n", "\n\t")) + print("reason:", e.reason) + user = password = None + +if not fs.logged_in: + print("Please sign in to FamilySearch.") + +while not fs.logged_in: try: fs.login(input("Username: "), getpass()) except HTTPError: print("Not logged in. Try again.") me = fs.get_current_user()['response']['users'][0]['displayName'] -print("Welcome, " + me + "!") \ No newline at end of file +print("Welcome, " + me + "!") From d928399359c25d1e6ef2c34419212b6da7e4571a Mon Sep 17 00:00:00 2001 From: Doug Henderson Date: Sat, 21 May 2016 02:42:08 -0600 Subject: [PATCH 04/14] add Python 3.5 --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ef2b550..e5035d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +5,12 @@ python: - "3.2" - "3.3" - "3.4" + - "3.5" install: pip install coveralls # SDK does not have any external dependencies. - + script: coverage run --source=familysearch setup.py test after_success: coveralls From 98cd7f134cf033023519150cdfff30af26786123 Mon Sep 17 00:00:00 2001 From: Doug Henderson Date: Sat, 21 May 2016 02:49:47 -0600 Subject: [PATCH 05/14] update for my fork, add Python 3.5, fix keyword typo --- setup.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 23d207f..b91b9a7 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + try: # Setuptools or Distribute is required to support running `python setup.py test` from setuptools import setup @@ -15,11 +17,11 @@ version = version, description = 'A Python SDK for FamilySearch.org', long_description=open('README.rst').read(), - author = 'Elder Evans', - author_email = 'elderamevans@gmail.com', - url = 'https://github.com/elderamevans/familysearch-python-sdk-opensource', - download_url = 'https://github.com/elderamevans/familysearch-python-sdk-opensource/releases/tag/v' + version, - keywords = ['FamilySearch', 'API', 'REST', 'family history', 'geneaolgy', 'JSON'], + author = 'Doug Henderson, Elder Evans, Peter Henderson', + author_email = 'djhenderson on github.com', + url = 'https://github.com/djhenderson/familysearch-python-sdk-opensource', + download_url = 'https://github.com/djhenderson/familysearch-python-sdk-opensource/releases/tag/v' + version, + keywords = ['FamilySearch', 'API', 'REST', 'family history', 'genealogy', 'JSON'], classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", @@ -36,6 +38,7 @@ "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", "Topic :: Internet :: WWW/HTTP", "Topic :: Sociology :: Genealogy", "Topic :: Software Development :: Libraries :: Python Modules" From 430558bb66d39a8f757ec6e7715f17f06be01510 Mon Sep 17 00:00:00 2001 From: Doug Henderson Date: Sat, 21 May 2016 02:53:53 -0600 Subject: [PATCH 06/14] customize for my fork --- README.rst | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index 1f6bdf9..896c89a 100644 --- a/README.rst +++ b/README.rst @@ -4,19 +4,19 @@ familysearch-python-sdk-opensource .. image:: https://readthedocs.org/projects/familysearch-python-sdk-opensource/badge/?version=latest :target: https://readthedocs.org/projects/familysearch-python-sdk-opensource/?badge=latest :alt: Documentation Status - + .. image:: https://www.codacy.com/project/badge/4875862e69c54164be173a94def06f09 - :target: https://www.codacy.com/app/elderamevans/familysearch-python-sdk-opensource + :target: https://www.codacy.com/app/djhenderson/familysearch-python-sdk-opensource :alt: Codacy Badge -.. image:: https://travis-ci.org/elderamevans/familysearch-python-sdk-opensource.svg?branch=master - :target: https://travis-ci.org/elderamevans/familysearch-python-sdk-opensource +.. image:: https://travis-ci.org/djhenderson/familysearch-python-sdk-opensource.svg?branch=master + :target: https://travis-ci.org/djhenderson/familysearch-python-sdk-opensource :alt: Build status -.. image:: https://coveralls.io/repos/elderamevans/familysearch-python-sdk-opensource/badge.svg - :target: https://coveralls.io/r/elderamevans/familysearch-python-sdk-opensource +.. image:: https://coveralls.io/repos/djhenderson/familysearch-python-sdk-opensource/badge.svg + :target: https://coveralls.io/r/djhenderson/familysearch-python-sdk-opensource :alt: Code Coverage - + .. image:: https://img.shields.io/pypi/v/familysearch-python-sdk-opensource.svg :target: https://pypi.python.org/pypi/familysearch-python-sdk-opensource :alt: PyPI Version @@ -25,13 +25,12 @@ familysearch-python-sdk-opensource :target: https://pypi.python.org/pypi/familysearch-python-sdk-opensource -This is a reboot/spinoff of https://github.com/familysearch-devnet/python-fs-stack. +This is a reboot/spinoff of https://github.com/familysearch-devnet/python-fs-stack which was last updated about June 8, 2011. + +This is a fork of https://github.com/AmEv7Fam/familysearch-python-sdk-opensource which was last updated about July 21, 2015. May or may not have all of the latest FamilySearch endpoints in place, but it is flexible enough to allow for additional endpoints It is designed for Python 3 and 2.7. Python 2.6 is the earliest supported version, but bugfixes are low-priority. 2.5 and earlier are not guaranteed to work, or supported, and are considered legacy. -To get started, visit http://elderamevans.github.io/familysearch-python-sdk-opensource. - - - +To get started, visit http://elderamevans.github.io/familysearch-python-sdk-opensource (May be 404 Not found). From 9ab775f8a8c8f6a5f3e0ba2de323cb7a7e6645f0 Mon Sep 17 00:00:00 2001 From: Doug Henderson Date: Sat, 21 May 2016 02:56:25 -0600 Subject: [PATCH 07/14] update version, apply pending pull request to fix 2.7 login failure --- familysearch/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/familysearch/__init__.py b/familysearch/__init__.py index 9ab83d4..ecec929 100644 --- a/familysearch/__init__.py +++ b/familysearch/__init__.py @@ -64,7 +64,7 @@ # Magic -__version__ = '1.2.0' +__version__ = '1.3.0alpha' class Request(BaseRequest): @@ -120,13 +120,13 @@ def __init__(self, agent, key, session=None, self.logged_in = bool(self.access_token) Discovery.__init__(self) - super().__init__() + super(FamilySearch, self).__init__() # Discovery needs to explicitly be first, as it is the hypermedia # engine. # There might be a better fix, but it's better than nothing. - + def _request(self, url, data=None, headers=None, method=None, nojson=False): """ Make a request to the FamilySearch API. From e33477ad1936908b7817bb218310abf482f5ac04 Mon Sep 17 00:00:00 2001 From: Doug Henderson Date: Sat, 21 May 2016 02:57:47 -0600 Subject: [PATCH 08/14] attempt to get authentication to work --- familysearch/authentication.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/familysearch/authentication.py b/familysearch/authentication.py index 73c3198..aa6fea4 100644 --- a/familysearch/authentication.py +++ b/familysearch/authentication.py @@ -1,5 +1,6 @@ """FamilySearch Authentication submodule""" # Python imports +from __future__ import print_function try: # Python 3 @@ -43,7 +44,12 @@ def login(self, username, password): }) credentials = credentials.encode("utf-8") headers = {"Content-type": "application/x-www-form-urlencoded", - "Accept": "application/json"} + "Accept": "application/json;charset=ISO-8859-1"} + def xprint(): + print("url:", url) + print("credentials:", credentials) + print("headers:", headers) + xprint() response = self.post(url, credentials, headers) self.access_token = response['response']['access_token'] self.logged_in = True @@ -141,4 +147,3 @@ def do_GET(self): sendme += "

You can safely close this page.

" sendme = sendme.encode("UTF-8") self.wfile.write(sendme) - \ No newline at end of file From ddc5001d9d3d3de9e97a1ba9929da923d2f8199b Mon Sep 17 00:00:00 2001 From: Doug Henderson Date: Sat, 21 May 2016 03:56:05 -0600 Subject: [PATCH 09/14] add coding where missing, and fix most pep8 complaints excepting E501. --- familysearch/__init__.py | 11 +++--- familysearch/authentication.py | 36 +++++++++++++------- familysearch/authorities.py | 10 ++++-- familysearch/changeHistory.py | 8 +++-- familysearch/discovery.py | 10 ++++-- familysearch/discussions.py | 6 +++- familysearch/memories.py | 10 ++++-- familysearch/ordinances.py | 6 +++- familysearch/parentsAndChildren.py | 12 ++++--- familysearch/pedigree.py | 6 +++- familysearch/person.py | 14 +++++--- familysearch/places.py | 6 +++- familysearch/records.py | 4 +++ familysearch/searchAndMatch.py | 6 +++- familysearch/sources.py | 6 +++- familysearch/spouses.py | 8 +++-- familysearch/user.py | 20 ++++++----- familysearch/utilities.py | 8 +++-- familysearch/vocabularies.py | 8 +++-- sample-apps/web_person/web-person.py | 19 ++++++----- sample-apps/whoami/whoami.py | 2 +- test/test_basefs.py | 5 ++- test/test_enhanced_request.py | 50 +++++++++++++--------------- test/util.py | 2 ++ 24 files changed, 178 insertions(+), 95 deletions(-) diff --git a/familysearch/__init__.py b/familysearch/__init__.py index ecec929..56afc98 100644 --- a/familysearch/__init__.py +++ b/familysearch/__init__.py @@ -1,6 +1,9 @@ +# -*- coding: utf-8 -*- + # For PyLint users: # I'll get back to you to the recommeneded command. -r"""This is a WIP, unofficial SDK for accessing + +"""This is a WIP, unofficial SDK for accessing the FamilySearch API. Currently designed to support Python 3.2+ and 2.6+. @@ -24,7 +27,6 @@ # Python imports - try: # Python 3 from urllib.request import Request as BaseRequest @@ -126,7 +128,6 @@ def __init__(self, agent, key, session=None, # There might be a better fix, but it's better than nothing. - def _request(self, url, data=None, headers=None, method=None, nojson=False): """ Make a request to the FamilySearch API. @@ -148,7 +149,7 @@ def _request(self, url, data=None, headers=None, method=None, nojson=False): try: data = data.encode('utf-8') except AttributeError: - #Some things are not JSON, and need to be sent as bytes! + # Some things are not JSON, and need to be sent as bytes! pass request = Request(url, data, headers, method=method) if not nojson: @@ -167,7 +168,7 @@ def _request(self, url, data=None, headers=None, method=None, nojson=False): if error.code == 401: self.logged_in = False if error.code == 429: - time.sleep(eh['Retry-after']/1000) + time.sleep(eh['Retry-after'] / 1000) return self._request(url, data, headers, method, nojson) raise diff --git a/familysearch/authentication.py b/familysearch/authentication.py index aa6fea4..5de34a4 100644 --- a/familysearch/authentication.py +++ b/familysearch/authentication.py @@ -1,4 +1,7 @@ +# -*- coding: utf-8 -*- + """FamilySearch Authentication submodule""" + # Python imports from __future__ import print_function @@ -19,13 +22,15 @@ # Magic + class Authentication(object): + def __init__(self): """https://familysearch.org/developers/docs/api/resources#authentication Set up the URLs for authentication. """ self.token = self.root_collection['response']['collections'][0]['links']\ - ['http://oauth.net/core/2.0/endpoint/token']['href'] + ['http://oauth.net/core/2.0/endpoint/token']['href'] cookie_handler = HTTPCookieProcessor() self.cookies = cookie_handler.cookiejar self.opener = build_opener(cookie_handler) @@ -41,14 +46,17 @@ def login(self, username, password): 'password': password, 'client_id': self.key, 'grant_type': 'password' - }) + } + ) credentials = credentials.encode("utf-8") headers = {"Content-type": "application/x-www-form-urlencoded", - "Accept": "application/json;charset=ISO-8859-1"} + "Accept": "application/json;charset=ISO-8859-1"} + def xprint(): print("url:", url) print("credentials:", credentials) print("headers:", headers) + xprint() response = self.post(url, credentials, headers) self.access_token = response['response']['access_token'] @@ -69,11 +77,13 @@ def oauth_desktop_login(self, ruri=None): self.logged_in = False self.cookies.clear() url = self.root_collection['response']['collections'][0]['links']\ - ['http://oauth.net/core/2.0/endpoint/authorize']['href'] - url = self._add_query_params(url, {'response_type': 'code', - 'client_id': self.key, - 'redirect_uri': ruri - }) + ['http://oauth.net/core/2.0/endpoint/authorize']['href'] + url = self._add_query_params(url, + {'response_type': 'code', + 'client_id': self.key, + 'redirect_uri': ruri, + } + ) webbrowser.open(url) server.HTTPServer(('', 63342), Getter).handle_request() # Now that we have the authentication token, grab the access token. @@ -87,12 +97,13 @@ def oauth_code_login(self, code): url = self.token credentials = urlencode({'grant_type': 'authorization_code', 'code': code, - 'client_id': self.key - }) + 'client_id': self.key, + } + ) credentials = credentials.encode("utf-8") headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "application/json"} - response = self.post(url, credentials ,headers, nojson=True) + response = self.post(url, credentials, headers, nojson=True) response = json.loads() self.access_token = response['response']['access_token'] self.logged_in = True @@ -108,7 +119,7 @@ def unauthenticated_login(self, ip_address): self.cookies.clear() url = self.token credentials = urlencode({'ip_address': ip_address, - #TODO: make IP address generiation automatic + # TODO: make IP address generiation automatic 'client_id': self.key, 'grant_type': 'unauthenticated_session' }) @@ -131,6 +142,7 @@ def logout(self): self.cookies.clear() self.fix_discovery() + class Getter(server.BaseHTTPRequestHandler): """Sample login page, mostly for oauth_desktop_login.""" def do_GET(self): diff --git a/familysearch/authorities.py b/familysearch/authorities.py index 232837a..9b1213a 100644 --- a/familysearch/authorities.py +++ b/familysearch/authorities.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Authorities submodule""" + # Python imports - + # Magic + class Authorities(): """https://familysearch.org/developers/docs/api/resources#authorities""" def __init__(self): @@ -11,7 +15,7 @@ def __init__(self): def dates(self, **kwargs): """https://familysearch.org/developers/docs/api/dates/Date_resource.""" - + try: url = self.collections['FSDA']['response']['collections'][0][ 'links']['normalized-date']['template'] @@ -21,4 +25,4 @@ def dates(self, **kwargs): 'links']['normalized-date']['template'] shim = {} shim["?date,access_token"] = "" - return self._add_query_params(url.format(**shim), kwargs) \ No newline at end of file + return self._add_query_params(url.format(**shim), kwargs) diff --git a/familysearch/changeHistory.py b/familysearch/changeHistory.py index ab4152f..2d52320 100644 --- a/familysearch/changeHistory.py +++ b/familysearch/changeHistory.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Change History submodule""" + # Python imports # Magic + class ChangeHistory: """https://familysearch.org/developers/docs/api/resources#authorities""" def __init__(self): @@ -11,7 +15,7 @@ def __init__(self): def person_change_history(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Change_History_resource""" - return self.base + "/platform/tree/persons/"+ pid +"/changes" + return self.base + "/platform/tree/persons/" + pid + "/changes" def child_change_history(self, caprid): """https://familysearch.org/developers/docs/api/tree/Child-and-Parents_Relationship_Change_History_resource""" @@ -26,4 +30,4 @@ def couple_change_history(self, crid, **kwargs): def restore_change(self, chid): """https://familysearch.org/developers/docs/api/tree/Restore_Change_resource""" - return self.tree_base + "changes/" + chid + "/restore" \ No newline at end of file + return self.tree_base + "changes/" + chid + "/restore" diff --git a/familysearch/discovery.py b/familysearch/discovery.py index a5a542b..d29569d 100644 --- a/familysearch/discovery.py +++ b/familysearch/discovery.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Discovery submodule""" + # Python imports # Magic + class Discovery(object): """https://familysearch.org/developers/docs/api/tree/FamilySearch_Collections_resource""" def __init__(self): @@ -11,8 +15,8 @@ def __init__(self): # until absolutely necessary... self.root_collection = self.get(self.base + '/.well-known/collection') self.subcollections = self.get(self.root_collection['response'] - ['collections'][0]['links'] - ['subcollections']['href']) + ['collections'][0]['links'] + ['subcollections']['href']) self.collections = {} self.fix_discovery() @@ -37,4 +41,4 @@ def fix_discovery(self): try: self.user = self.get_current_user()['response']['users'][0] except: - self.user = "" \ No newline at end of file + self.user = "" diff --git a/familysearch/discussions.py b/familysearch/discussions.py index 5caaaad..db484e2 100644 --- a/familysearch/discussions.py +++ b/familysearch/discussions.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Discussions submodule""" + # Python imports # Magic + class Discussions: """https://familysearch.org/developers/docs/api/resources#discussions""" def __init__(self): @@ -24,4 +28,4 @@ def discussion_comments(self, did): def discussion_comment(self, did, cmid): """https://familysearch.org/developers/docs/api/discussions/Comment_resource""" - return self.discuss_base + did + "/comments/" + cmid \ No newline at end of file + return self.discuss_base + did + "/comments/" + cmid diff --git a/familysearch/memories.py b/familysearch/memories.py index d9c2ac5..a4b80c6 100644 --- a/familysearch/memories.py +++ b/familysearch/memories.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Memories submodule""" + # Python imports # Magic + class Memories: """https://familysearch.org/developers/docs/api/resources#memories""" def __init__(self): @@ -26,15 +30,15 @@ def user_memories(self, **kwargs): def memory_personas(self, mid): """https://familysearch.org/developers/docs/api/memories/Memory_Personas_resource""" return self.memory_base + mid + "personas" - + def memory_persona(self, mid, pid): """https://familysearch.org/developers/docs/api/memories/Memory_Persona_resource""" return self.memory_base + mid + "personas/" + pid - + def memory_comments(self, mid): """https://familysearch.org/developers/docs/api/memories/Memory_Comments_resource""" return self.memory_base + mid + "comments" def memories_comment(self, mid, cmid): """https://familysearch.org/developers/docs/api/memories/Memories_Comment_resource""" - return self.memory_base + mid + "comments/" + cmid \ No newline at end of file + return self.memory_base + mid + "comments/" + cmid diff --git a/familysearch/ordinances.py b/familysearch/ordinances.py index 5f79046..8419708 100644 --- a/familysearch/ordinances.py +++ b/familysearch/ordinances.py @@ -1,10 +1,14 @@ +# -*- coding: utf-8 -*- + """FamilySearch Ordinances submodule""" + # Python imports # Magic + class Ordinances: """https://familysearch.org/developers/docs/api/resources#ordinances""" def __init__(self): """https://familysearch.org/developers/docs/api/examples#ordinances""" - pass # Placeholder file. \ No newline at end of file + pass # Placeholder file. diff --git a/familysearch/parentsAndChildren.py b/familysearch/parentsAndChildren.py index d44c757..95b98d4 100644 --- a/familysearch/parentsAndChildren.py +++ b/familysearch/parentsAndChildren.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Parents and Children submodule""" + # Python imports # Magic + class ParentsAndChildren: """https://familysearch.org/developers/docs/api/resources#parents-and-children""" def __init__(self): @@ -20,16 +24,16 @@ def child_relationship_parent(self, crid, role): def child_relationship_conclusion(self, crid, role, cid): """https://familysearch.org/developers/docs/api/tree/Child-and-Parents_Relationship_Conclusion_resource""" return self.delete( - self.child_base + crid + "/" + role + "/conclusions/" + cid) - + self.child_base + crid + "/" + role + "/conclusions/" + cid) + def child_relationship_notes(self, crid): """https://familysearch.org/developers/docs/api/tree/Child-and-Parents_Relationship_Notes_resource""" return self.child_base + crid + "/notes" - + def child_relationship_note(self, crid, nid): """https://familysearch.org/developers/docs/api/tree/Child-and-Parents_Relationship_Note_resource""" return self.child_base + crid + "/notes/" + nid def child_relationship_restore(self, crid): """https://familysearch.org/developers/docs/api/tree/Child-and-Parents_Relationship_Restore_resource""" - return self.child_base + crid + "/restore" \ No newline at end of file + return self.child_base + crid + "/restore" diff --git a/familysearch/pedigree.py b/familysearch/pedigree.py index c718eee..f481015 100644 --- a/familysearch/pedigree.py +++ b/familysearch/pedigree.py @@ -1,15 +1,19 @@ +# -*- coding: utf-8 -*- + """FamilySearch Pedigree submodule""" + # Python imports # Magic + class Pedigree: """https://familysearch.org/developers/docs/api/examples#pedigree""" def __init__(self): """https://familysearch.org/developers/docs/api/examples#pedigree""" pass - def ancestry(self, pid,**kwargs): + def ancestry(self, pid, **kwargs): """https://familysearch.org/developers/docs/api/tree/Ancestry_resource""" return self._add_query_params( self.tree_base + 'ancestry?person=' + pid, kwargs) diff --git a/familysearch/person.py b/familysearch/person.py index 45e8945..930fcfb 100644 --- a/familysearch/person.py +++ b/familysearch/person.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Person submodule""" + # Python imports # Magic + class Person: """https://familysearch.org/developers/docs/api/examples#person""" def __init__(self): @@ -27,7 +31,7 @@ def person_spouses(self, pid): def person_children(self, pid): """https://familysearch.org/developers/docs/api/tree/Children_of_a_Person_resource""" - return self.get(self.person_base+pid+"/children") + return self.get(self.person_base + pid + "/children") def spouse_relationships(self, pid, **kwargs): """https://familysearch.org/developers/docs/api/tree/Person_Relationships_to_Spouses_resource""" @@ -52,7 +56,7 @@ def person_with_relationships(self, **kwargs): def person_conclusion(self, pid, cid): """https://familysearch.org/developers/docs/api/tree/Person_Conclusion_resource""" return self.person_base + pid + "conclusions" + cid - + def person_source_references(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Source_References_resource""" return self.person_base + pid + "source-references" @@ -60,7 +64,7 @@ def person_source_references(self, pid): def person_source_reference(self, pid, srid): """https://familysearch.org/developers/docs/api/tree/Person_Source_References_resource""" return self.person_base + pid + "source-references/" + srid - + def person_sources_query(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Sources_Query_resource""" return self.person_base + pid + "sources" @@ -104,7 +108,7 @@ def preferred_parent_relationship(self, pid, uid=None): """https://familysearch.org/developers/docs/api/tree/Preferred_Parent_Relationship_resource""" if uid is None: uid = self.user['userId'] - return self.user_base + uid +"/preferred-parent-relationships/" + pid + return self.user_base + uid + "/preferred-parent-relationships/" + pid def person_memories(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Memories_resource""" @@ -125,4 +129,4 @@ def person_memories_portrait(self, pid, **kwargs): def person_portraits(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Portraits_resource""" - return self.person_base + "/portrait" \ No newline at end of file + return self.person_base + "/portrait" diff --git a/familysearch/places.py b/familysearch/places.py index 86477f7..31025f2 100644 --- a/familysearch/places.py +++ b/familysearch/places.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Places submodule""" + # Python imports # Magic + class Places: """https://familysearch.org/developers/docs/api/resources#places""" def __init__(self): @@ -43,4 +47,4 @@ def place_types(self): def place_type_groups(self): """https://familysearch.org/developers/docs/api/places/Place_Type_Groups_resource""" - return self.places_base + "type-groups" \ No newline at end of file + return self.places_base + "type-groups" diff --git a/familysearch/records.py b/familysearch/records.py index 322b5cc..b95455e 100644 --- a/familysearch/records.py +++ b/familysearch/records.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Records submodule""" + # Python imports # Magic + class Records: """https://familysearch.org/developers/docs/api/resources#records""" def __init__(self): diff --git a/familysearch/searchAndMatch.py b/familysearch/searchAndMatch.py index cacac9d..58942a1 100644 --- a/familysearch/searchAndMatch.py +++ b/familysearch/searchAndMatch.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Search and Match submodule""" + # Python imports # Magic + class SearchAndMatch: """https://familysearch.org/developers/docs/api/resources#search-and-match""" def __init__(self): @@ -23,4 +27,4 @@ def person_not_a_match_list(self, pid): def person_matches_query(self, **kwargs): """https://familysearch.org/developers/docs/api/tree/Person_Matches_Query_resource""" - return self._add_query_params(self.tree_base + "matches", kwargs) \ No newline at end of file + return self._add_query_params(self.tree_base + "matches", kwargs) diff --git a/familysearch/sources.py b/familysearch/sources.py index 463cfa7..363036d 100644 --- a/familysearch/sources.py +++ b/familysearch/sources.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Vocabularies submodule""" + # Python imports # Magic + class Sources: """https://familysearch.org/developers/docs/api/resources#sources""" def __init__(self): @@ -42,4 +46,4 @@ def user_source_descriptions(self, **kwargs): def source_references_query(self, **kwargs): """https://familysearch.org/developers/docs/api/tree/Source_References_Query_resource""" return self._add_query_params( - self.tree_base + "source_references", kwargs) \ No newline at end of file + self.tree_base + "source_references", kwargs) diff --git a/familysearch/spouses.py b/familysearch/spouses.py index d7a1ee3..ea2ab11 100644 --- a/familysearch/spouses.py +++ b/familysearch/spouses.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Spouses submodule""" + # Python imports # Magic + class Spouses: """https://familysearch.org/developers/docs/api/resources#spouses""" def __init__(self): @@ -20,7 +24,7 @@ def couple_relationship(self, cpid, **kwargs): def couple_relationship_conclusion(self, cpid, cid): """https://familysearch.org/developers/docs/api/tree/Couple_Relationship_Conclusion_resource""" return self.couple_base + cpid + '/conclusions/' + cid - + def couple_relationship_notes(self, crid): """https://familysearch.org/developers/docs/api/tree/Couple_Relationship_Notes_resource""" return self.couple_base + crid + "/notes" @@ -31,4 +35,4 @@ def couple_relationship_note(self, crid): def couple_relationship_restore(self, cpid): """https://familysearch.org/developers/docs/api/tree/Couple_Relationship_Restore_resource""" - return self.couple_base + cpid + '/restore' \ No newline at end of file + return self.couple_base + cpid + '/restore' diff --git a/familysearch/user.py b/familysearch/user.py index 5ae322f..492ebad 100644 --- a/familysearch/user.py +++ b/familysearch/user.py @@ -1,10 +1,12 @@ -"""FamilySearch User submodule""" -# Python imports +# -*- coding: utf-8 -*- +"""FamilySearch User submodule""" +# Python imports # Magic + class User(object): """https://familysearch.org/developers/docs/api/resources#user""" def __init__(self): @@ -14,18 +16,18 @@ def __init__(self): def current_user(self): """https://familysearch.org/developers/docs/api/users/Current_User_resource""" url = self.root_collection['response']['collections'][0]['links']\ - ['current-user']['href'] + ['current-user']['href'] return url - + def current_user_person(self): """https://familysearch.org/developers/docs/api/tree/Current_Tree_Person_resource""" try: url = self.collections["FSFT"]["response"]["collections"][0][ - "links"]["current-user-person"]["href"] + "links"]["current-user-person"]["href"] except KeyError: self.update_collection("FSFT") url = self.collections["FSFT"]["response"]["collections"][0][ - "links"]["current-user-person"]["href"] + "links"]["current-user-person"]["href"] return url def agent(self, uid): @@ -36,9 +38,9 @@ def current_user_history(self): """https://familysearch.org/developers/docs/api/users/Current_User_History_resource""" try: url = self.collections["FSFT"]["response"]["collections"][0][ - "links"]["current-user-history"]["href"] + "links"]["current-user-history"]["href"] except KeyError: self.update_collection("FSFT") url = self.collections["FSFT"]["response"]["collections"][0][ - "links"]["current-user-history"]["href"] - return url \ No newline at end of file + "links"]["current-user-history"]["href"] + return url diff --git a/familysearch/utilities.py b/familysearch/utilities.py index ad4c4b9..172350b 100644 --- a/familysearch/utilities.py +++ b/familysearch/utilities.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Utilities submodule""" + # Python imports # Magic + class Utilities: """https://familysearch.org/developers/docs/api/resources#utilities""" def __init__(self): @@ -18,8 +22,8 @@ def redirect(self, **kwargs): """https://familysearch.org/developers/docs/api/tree/Redirect_resource""" return self._add_query_params( self.base + "/platform/redirect", kwargs) - + def oembed(self, **kwargs): """https://familysearch.org/developers/docs/api/discovery/OEmbed_resource""" return self._add_query_params( - self.base + "/platform/oembed", kwargs) \ No newline at end of file + self.base + "/platform/oembed", kwargs) diff --git a/familysearch/vocabularies.py b/familysearch/vocabularies.py index 31c451f..f6266e2 100644 --- a/familysearch/vocabularies.py +++ b/familysearch/vocabularies.py @@ -1,8 +1,12 @@ +# -*- coding: utf-8 -*- + """FamilySearch Vocabularies submodule""" + # Python imports # Magic + class Vocabularies: """https://familysearch.org/developers/docs/api/resources#vocabularies""" def __init__(self): @@ -22,7 +26,7 @@ def vocabulary_list(self, cvlid): shim["?access_token"] = "" shim["cvlid"] = cvlid return url.format(**shim) - + def vocabulary_lists(self): """https://familysearch.org/developers/docs/api/cv/Controlled_Vocabulary_List_resource""" try: @@ -36,7 +40,7 @@ def vocabulary_lists(self): def vocabulary_term(self, cvtid): """https://familysearch.org/developers/docs/api/cv/Controlled_Vocabulary_Term_resource""" - #return self.vocab_base + "lists/" + cvtid + # return self.vocab_base + "lists/" + cvtid """https://familysearch.org/developers/docs/api/cv/Controlled_Vocabulary_List_resource""" try: url = self.collections["FSCV"]["response"]['collections'][ diff --git a/sample-apps/web_person/web-person.py b/sample-apps/web_person/web-person.py index a4a4828..ed07db8 100644 --- a/sample-apps/web_person/web-person.py +++ b/sample-apps/web_person/web-person.py @@ -50,6 +50,7 @@ print("KeyError:", str(e)) raise + def qshow(): def hr(): print("="*80) hr() @@ -77,10 +78,12 @@ def hr(): print("="*80) hr() qshow() -fslogin = fs._add_query_params(fslogin, {'response_type': 'code', - 'client_id': fs.key, - 'redirect_uri': redirect - }) +fslogin = fs._add_query_params(fslogin, { + 'response_type': 'code', + 'client_id': fs.key, + 'redirect_uri': redirect + }) + class getter(server.BaseHTTPRequestHandler): def do_GET(self): @@ -118,8 +121,8 @@ def not_logged_in(self): def logged_in(self): string = 'Search given FamilySearch PID (default is your own)' - string +=' Date: Mon, 23 May 2016 23:14:40 -0600 Subject: [PATCH 10/14] clean up and add some missing bits --- docs/Authentication.md | 23 +++++++++++++++++++++++ docs/Authorities.md | 1 + docs/Discovery.md | 5 +++++ docs/Memories.md | 4 ++-- docs/Person.md | 6 +++--- docs/Places.md | 2 +- docs/Sources.md | 10 +++++----- docs/index.md | 7 +++++-- 8 files changed, 45 insertions(+), 13 deletions(-) diff --git a/docs/Authentication.md b/docs/Authentication.md index e69de29..8ac91d0 100644 --- a/docs/Authentication.md +++ b/docs/Authentication.md @@ -0,0 +1,23 @@ +* `login(self, username, password)` +Log into FamilySearch using Basic Authentication. +This mechanism is available only to approved developer keys. + +* `oauth_desktop_login(self, ruri=None)` +Log into FamilySearch using OAuth2 Authentication. +This is primarily a convenience function for desktop apps. +Not normally intended for production apps, but should +work while waiting for approval for password login. +Default Redirect URI is "http://localhost:63342/fslogin", +but you can set your own as a paramater. + +* `oauth_code_login(self, code)` +Convenience function for Web servers to log into FamilySearch +with the token code FamilySearch hands you. + +* `unauthenticated_login(self, ip_address)` +Log into FamilySearch without authenticating. +Has very limited read-only access. +Not intended for general use. + +* `logout(self)` +Log the current session out of FamilySearch. diff --git a/docs/Authorities.md b/docs/Authorities.md index e69de29..6a5b2f2 100644 --- a/docs/Authorities.md +++ b/docs/Authorities.md @@ -0,0 +1 @@ +* [`dates(self, **kwargs)`](https://familysearch.org/developers/docs/api/dates/Date_resource) diff --git a/docs/Discovery.md b/docs/Discovery.md index e69de29..099bc04 100644 --- a/docs/Discovery.md +++ b/docs/Discovery.md @@ -0,0 +1,5 @@ +* `update_collection(self, collection)` + +* `fix_discovery(self)` +The Hypermedia items are semi-permanent. Some things change +based on who's logged in (or out). diff --git a/docs/Memories.md b/docs/Memories.md index cf8841f..77406cd 100644 --- a/docs/Memories.md +++ b/docs/Memories.md @@ -1,7 +1,7 @@ * [`memories(**kwargs)`](https://familysearch.org/developers/docs/api/memories/Memories_resource) * [`memory(mid)`](https://familysearch.org/developers/docs/api/memories/Memory_resource) * [`user_memories(**kwargs)`](https://familysearch.org/developers/docs/api/memories/User_Memories_resource) -* [`memory_personas(mid):`](https://familysearch.org/developers/docs/api/memories/Memory_Personas_resource) +* [`memory_personas(mid)`](https://familysearch.org/developers/docs/api/memories/Memory_Personas_resource) * [`memory_persona(mid, pid)`](https://familysearch.org/developers/docs/api/memories/Memory_Persona_resource) * [`memory_comments(mid)`](https://familysearch.org/developers/docs/api/memories/Memory_Comments_resource) -* [`memories_comment(mid, cmid)`](https://familysearch.org/developers/docs/api/memories/Memories_Comment_resource) \ No newline at end of file +* [`memories_comment(mid, cmid)`](https://familysearch.org/developers/docs/api/memories/Memories_Comment_resource) diff --git a/docs/Person.md b/docs/Person.md index 0e6bbdb..4d8c56d 100644 --- a/docs/Person.md +++ b/docs/Person.md @@ -16,12 +16,12 @@ * [`person_discussion_reference(pid, drid)`](https://familysearch.org/developers/docs/api/tree/Person_Discussion_References_resource) * [`person_merge(pid, dpid, **kwargs)`](https://familysearch.org/developers/docs/api/tree/Person_Merge_resource) * [`person_change_summary(pid)`](https://familysearch.org/developers/docs/api/tree/Person_Merge_resource) -* [`person_not_a_match(pid, dpid):`](https://familysearch.org/developers/docs/api/tree/Person_Not_A_Match_resource) +* [`person_not_a_match(pid, dpid)`](https://familysearch.org/developers/docs/api/tree/Person_Not_A_Match_resource) * [`person_restore(pid)`](https://familysearch.org/developers/docs/api/tree/Person_Restore_resource) * [`preferred_spouse_relationship(pid)`](https://familysearch.org/developers/docs/api/tree/Preferred_Spouse_Relationship_resource) * [`preferred_parent_relationship(pid)`](https://familysearch.org/developers/docs/api/tree/Preferred_Parent_Relationship_resource) -* [`person_memories(pid)`](https://familysearch.org/developers/docs/api/tree/Person_Memories_resource) +* [`person_memories(pid, **kwargs)`](https://familysearch.org/developers/docs/api/tree/Person_Memories_resource) * [`person_memory_references(pid)`](https://familysearch.org/developers/docs/api/tree/Person_Memory_References_resource) * [`person_memory_reference(pid, erid)`](https://familysearch.org/developers/docs/api/tree/Person_Memory_References_resource) * [`person_memories_portrait(pid, **kwargs)`](https://familysearch.org/developers/docs/api/tree/Person_Memories_Portrait_resource) -* [`person_portraits(pid)`](https://familysearch.org/developers/docs/api/tree/Person_Portraits_resource) \ No newline at end of file +* [`person_portraits(pid)`](https://familysearch.org/developers/docs/api/tree/Person_Portraits_resource) diff --git a/docs/Places.md b/docs/Places.md index e98e0ae..72ed53f 100644 --- a/docs/Places.md +++ b/docs/Places.md @@ -3,7 +3,7 @@ * [`place_group(pgid)`](https://familysearch.org/developers/docs/api/places/Place_Group_resource) * [`place(pid)`](https://familysearch.org/developers/docs/api/places/Place_resource) * [`place_description_children()`](https://familysearch.org/developers/docs/api/places/Place_Description_Children_resource) -* [`place_type(ptid):`](https://familysearch.org/developers/docs/api/places/Place_Type_resource) +* [`place_type(ptid)`](https://familysearch.org/developers/docs/api/places/Place_Type_resource) * [`place_type_group(ptgid)`](https://familysearch.org/developers/docs/api/places/Place_Type_Group_resource) * [`place_types(self)`](https://familysearch.org/developers/docs/api/places/Place_Types_resource) * [`def place_type_groups(self):`](https://familysearch.org/developers/docs/api/places/Place_Type_Groups_resource) diff --git a/docs/Sources.md b/docs/Sources.md index 6cffa88..17939cd 100644 --- a/docs/Sources.md +++ b/docs/Sources.md @@ -1,8 +1,8 @@ * [`source_descriptions()`](https://familysearch.org/developers/docs/api/sources/Source_Descriptions_resource) -* [`source_description(sdid):`](https://familysearch.org/developers/docs/api/sources/Source_Description_resource) -* [`source_folders():`](https://familysearch.org/developers/docs/api/sources/Source_Folders_resource) -* [`source_folder(udcid):`](https://familysearch.org/developers/docs/api/sources/Source_Folder_resource) +* [`source_description(sdid)`](https://familysearch.org/developers/docs/api/sources/Source_Description_resource) +* [`source_folders()`](https://familysearch.org/developers/docs/api/sources/Source_Folders_resource) +* [`source_folder(udcid)`](https://familysearch.org/developers/docs/api/sources/Source_Folder_resource) * [`source_folder_source_descriptions(udcid, **kwargs)`](https://familysearch.org/developers/docs/api/sources/Source_Folder_Source_Descriptions_resource) * [`user_source_folders()`](https://familysearch.org/developers/docs/api/sources/User_Source_Folders_resource) -* [`user_source_descriptions(**kwargs):`](https://familysearch.org/developers/docs/api/sources/User_Source_Descriptions_resource) -* [`source_references_query(self, **kwargs):`](https://familysearch.org/developers/docs/api/tree/Source_References_Query_resource) \ No newline at end of file +* [`user_source_descriptions(**kwargs)`](https://familysearch.org/developers/docs/api/sources/User_Source_Descriptions_resource) +* [`source_references_query(self, **kwargs)`](https://familysearch.org/developers/docs/api/tree/Source_References_Query_resource) diff --git a/docs/index.md b/docs/index.md index 6d5c51c..f46a5c1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,5 +1,8 @@ -Modules are modeled after the layout of [the official FamilySearch API documentation](https://familysearch.org/developers/docs/api/resources). -If you're not sure which function you need, it's best to use the URL of the FamilySearch Developer documentation, and search in that section for that URL. +Modules are modeled after the layout of +[the official FamilySearch API documentation](https://familysearch.org/developers/docs/api/resources). +If you're not sure which function you need, +it's best to use the URL of the FamilySearch Developer documentation, +and search in that section for that URL. * [Main Module](Main_Module.md) * [Authentication](Authentication.md) From d2ca5f3189c03da341a2ba90234c35bca6af8d6c Mon Sep 17 00:00:00 2001 From: Doug Henderson Date: Mon, 23 May 2016 23:16:55 -0600 Subject: [PATCH 11/14] cleanup pylint stuff, fix code until login worked --- familysearch/__init__.py | 33 ++++++++++++++------- familysearch/authentication.py | 46 +++++++++++++++++++++--------- familysearch/authorities.py | 2 ++ familysearch/changeHistory.py | 6 ++++ familysearch/discovery.py | 10 ++++--- familysearch/discussions.py | 2 +- familysearch/memories.py | 9 ++++++ familysearch/ordinances.py | 2 ++ familysearch/parentsAndChildren.py | 9 ++++++ familysearch/pedigree.py | 4 +++ familysearch/person.py | 31 +++++++++++++++++++- familysearch/places.py | 10 +++++++ familysearch/records.py | 2 ++ familysearch/searchAndMatch.py | 7 +++++ familysearch/sources.py | 10 +++++++ familysearch/spouses.py | 8 ++++++ familysearch/user.py | 5 ++++ familysearch/utilities.py | 5 ++++ familysearch/vocabularies.py | 7 +++-- 19 files changed, 176 insertions(+), 32 deletions(-) diff --git a/familysearch/__init__.py b/familysearch/__init__.py index 56afc98..faac8bf 100644 --- a/familysearch/__init__.py +++ b/familysearch/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # For PyLint users: -# I'll get back to you to the recommeneded command. +# I'll get back to you to the recommended command. """This is a WIP, unofficial SDK for accessing the FamilySearch API. @@ -73,6 +73,7 @@ class Request(BaseRequest): """Add ability for the Request object to allow it to handle additional methods. """ + def __init__(self, *args, **kwargs): self._method = kwargs.pop('method', None) BaseRequest.__init__(self, *args, **kwargs) @@ -81,6 +82,7 @@ def get_method(self): """The Request object has been enhanced to handle PUT, DELETE, OPTIONS, and HEAD request methods. """ + if self._method: return self._method else: @@ -98,22 +100,21 @@ class FamilySearch(Authentication, Authorities, ChangeHistory, Discovery, ...needs to be re-written... """ - def __init__(self, agent, key, session=None, + def __init__(self, agent, dev_key, session=None, base='https://sandbox.familysearch.org'): - """Instantiate a FamilySearch proxy object. Keyword arguments: agent -- User-agent string to use for requests - key -- FamilySearch developer key + dev_key -- FamilySearch developer key (optional if reusing an existing session ID) session (optional) -- existing session ID to reuse base (optional) -- base URL for the API; defaults to 'https://sandbox.familysearch.org' """ - self.agent = '%s FSPySDK/%s' % (agent, __version__) - self.key = key + self.user_agent = '%s FSPySDK/%s' % (agent, __version__) + self.dev_key = dev_key self.access_token = session self.base = base self.user_base = self.base + "/platform/users/" @@ -138,6 +139,7 @@ def _request(self, url, data=None, headers=None, method=None, nojson=False): Returns a file-like object representing the response. """ + if headers is None: headers = {} if data: @@ -156,19 +158,21 @@ def _request(self, url, data=None, headers=None, method=None, nojson=False): if method is not "GET" and "Content-type" not in request.headers: request.add_header('Content-type', 'application/x-fs-v1+json') if 'Accept' not in request.headers: - request.add_header('Accept', 'application/json') + request.add_header('Accept', 'application/x-fs-v1+json;application/x-gedcomx-v1+json;application/x-gedcomx-atom+json;application/json') + if 'Accept-Charset' not in request.headers: + request.add_header('Accept-Charset', 'UTF-8') if self.logged_in and not self.cookies: # Add sessionId parameter to url if cookie is not set request.add_header('Authorization', 'Bearer ' + self.access_token) - request.add_header('User-Agent', self.agent) + request.add_header('User-Agent', self.user_agent) try: return self.opener.open(request) except HTTPError as error: - eh = dict(error.headers) + error_headers = dict(error.headers) if error.code == 401: self.logged_in = False if error.code == 429: - time.sleep(eh['Retry-after'] / 1000) + time.sleep(error_headers['Retry-after'] / 1000) return self._request(url, data, headers, method, nojson) raise @@ -178,8 +182,8 @@ def _add_subpath(self, url, subpath): For example, adding sub to http://example.com/path?query becomes http://example.com/path/sub?query. - """ + parts = urlsplit(url) path = parts[2] + '/' + subpath return urlunsplit((parts[0], parts[1], path, parts[3], parts[4])) @@ -188,6 +192,7 @@ def _add_query_params(self, url, params=None, **kw_params): """Add the specified query parameters to the given URL. Parameters can be passed either as a dictionary or as keyword arguments. """ + if params is None: params = {} parts = urlsplit(url) @@ -203,6 +208,7 @@ def _fs2py(self, response, nojson=False): Take JSON from FamilySearch response, and allow Python to handle it. Also, inject headers into response. """ + headers = dict(response.info()) response = response.read() response = response.decode("utf-8") @@ -219,25 +225,30 @@ def get(self, url, data=None, headers=None, nojson=False): def post(self, url, data=None, headers=None, nojson=False): """HTTP POST request""" + return self._fs2py(self._request( url, data, headers, "POST", nojson), nojson) def put(self, url, data=None, headers=None, nojson=False): """HTTP PUT request""" + return self._fs2py(self._request( url, data, headers, "PUT", nojson), nojson) def head(self, url, data=None, headers=None, nojson=False): """HTTP HEAD request""" + return self._fs2py(self._request( url, data, headers, "HEAD", nojson), nojson) def options(self, url, data=None, headers=None, nojson=False): """HTTP OPTIONS request""" + return self._fs2py(self._request( url, data, headers, "OPTIONS", nojson), nojson) def delete(self, url, data=None, headers=None, nojson=False): """HTTP DELETE request""" + return self._fs2py(self._request( url, data, headers, "DELETE", nojson), nojson) diff --git a/familysearch/authentication.py b/familysearch/authentication.py index 5de34a4..dcbd35f 100644 --- a/familysearch/authentication.py +++ b/familysearch/authentication.py @@ -29,28 +29,33 @@ def __init__(self): """https://familysearch.org/developers/docs/api/resources#authentication Set up the URLs for authentication. """ + self.token = self.root_collection['response']['collections'][0]['links']\ ['http://oauth.net/core/2.0/endpoint/token']['href'] cookie_handler = HTTPCookieProcessor() self.cookies = cookie_handler.cookiejar self.opener = build_opener(cookie_handler) + self.logged_in = False + self.access_token = None def login(self, username, password): """Log into FamilySearch using Basic Authentication. This mechanism is available only to approved developer keys. """ + self.logged_in = False self.cookies.clear() url = self.token credentials = urlencode({'username': username, 'password': password, - 'client_id': self.key, + 'client_id': self.dev_key, 'grant_type': 'password' } ) credentials = credentials.encode("utf-8") headers = {"Content-type": "application/x-www-form-urlencoded", - "Accept": "application/json;charset=ISO-8859-1"} + "Accept": "application/x-fs-v1+json;application/x-gedcomx-v1+json;application/x-gedcomx-atom+json;application/json", + "Accept-Charset": "utf-8"} def xprint(): print("url:", url) @@ -59,6 +64,7 @@ def xprint(): xprint() response = self.post(url, credentials, headers) + print("authentication.login:response:", response) self.access_token = response['response']['access_token'] self.logged_in = True self.fix_discovery() @@ -66,12 +72,13 @@ def xprint(): def oauth_desktop_login(self, ruri=None): """ Log into FamilySearch using OAuth2 Authentication. - This is primarily a convenience function for destop apps. + This is primarily a convenience function for desktop apps. Not normally intended for production apps, but should work while waiting for approval for password login. Default Redirect URI is "http://localhost:63342/fslogin", but you can set your own as a paramater. """ + if ruri is None: ruri = "http://localhost:63342/fslogin" self.logged_in = False @@ -80,7 +87,7 @@ def oauth_desktop_login(self, ruri=None): ['http://oauth.net/core/2.0/endpoint/authorize']['href'] url = self._add_query_params(url, {'response_type': 'code', - 'client_id': self.key, + 'client_id': self.dev_key, 'redirect_uri': ruri, } ) @@ -94,18 +101,26 @@ def oauth_code_login(self, code): Convenience function for Web servers to log into FamilySearch with the token code FamilySearch hands you. """ + url = self.token credentials = urlencode({'grant_type': 'authorization_code', 'code': code, - 'client_id': self.key, + 'client_id': self.dev_key, } ) credentials = credentials.encode("utf-8") headers = {"Content-type": "application/x-www-form-urlencoded", - "Accept": "application/json"} + "Accept": "application/x-fs-v1+json;application/x-gedcomx-v1+json;application/x-gedcomx-atom+json;application/json", + "Accept-Charset": "utf-8"} response = self.post(url, credentials, headers, nojson=True) - response = json.loads() - self.access_token = response['response']['access_token'] + def rprint(response): + import pprint + pp = pprint.PrettyPrinter(indent=2, width=120) + pp.pprint(response) + #rprint(response) + json_response = json.loads(response["response"]) + self.access_token = json_response['access_token'] + print("access_token:", self.access_token) self.logged_in = True self.fix_discovery() @@ -115,16 +130,18 @@ def unauthenticated_login(self, ip_address): Has very limited read-only access. Not intended for general use. """ + self.logged_in = False self.cookies.clear() url = self.token credentials = urlencode({'ip_address': ip_address, - # TODO: make IP address generiation automatic - 'client_id': self.key, + # TODO: make IP address generation automatic + 'client_id': self.dev_key, 'grant_type': 'unauthenticated_session' - }) + }) headers = {"Content-type": "application/x-www-form-urlencoded", - "Accept": "application/json"} + "Accept": "application/x-fs-v1+json;application/x-gedcomx-v1+json;application/x-gedcomx-atom+json;application/json", + "Accept-Charset": "utf-8"} credentials = credentials.encode("utf-8") response = self.post(url, credentials, headers) self.access_token = response['response']['access_token'] @@ -135,6 +152,7 @@ def logout(self): """ Log the current session out of FamilySearch. """ + self.logged_in = False url = self.token + "?access_token=" + self.access_token self.delete(url) @@ -145,10 +163,12 @@ def logout(self): class Getter(server.BaseHTTPRequestHandler): """Sample login page, mostly for oauth_desktop_login.""" + def do_GET(self): """Sample page to get Oauth code, and log in with.""" + self.send_response(code=200) - self.send_header("Content-type", "text/html") + self.send_header("Content-type", "text/html;charset=utf-8") self.end_headers() path = self.path global qs diff --git a/familysearch/authorities.py b/familysearch/authorities.py index 9b1213a..2e237ce 100644 --- a/familysearch/authorities.py +++ b/familysearch/authorities.py @@ -9,8 +9,10 @@ class Authorities(): """https://familysearch.org/developers/docs/api/resources#authorities""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#authorities.""" + pass def dates(self, **kwargs): diff --git a/familysearch/changeHistory.py b/familysearch/changeHistory.py index 2d52320..f0300d7 100644 --- a/familysearch/changeHistory.py +++ b/familysearch/changeHistory.py @@ -9,25 +9,31 @@ class ChangeHistory: """https://familysearch.org/developers/docs/api/resources#authorities""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#change-history""" + pass def person_change_history(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Change_History_resource""" + return self.base + "/platform/tree/persons/" + pid + "/changes" def child_change_history(self, caprid): """https://familysearch.org/developers/docs/api/tree/Child-and-Parents_Relationship_Change_History_resource""" + return self.tree_base + "child-and-parents-relationships/"\ + caprid + "/changes" def couple_change_history(self, crid, **kwargs): """https://familysearch.org/developers/docs/api/tree/Couple_Relationship_Change_History_resource""" + return self._add_query_params( self.tree_base + "couple-relationships/" + crid + "/changes", kwargs) def restore_change(self, chid): """https://familysearch.org/developers/docs/api/tree/Restore_Change_resource""" + return self.tree_base + "changes/" + chid + "/restore" diff --git a/familysearch/discovery.py b/familysearch/discovery.py index d29569d..80f4745 100644 --- a/familysearch/discovery.py +++ b/familysearch/discovery.py @@ -9,9 +9,11 @@ class Discovery(object): """https://familysearch.org/developers/docs/api/tree/FamilySearch_Collections_resource""" + def __init__(self): """https://familysearch.org/developers/docs/api/resources#discovery""" - # TODO: Set it up so that it doesn't need to call the sumbodules + + # TODO: Set it up so that it doesn't need to call the submodules # until absolutely necessary... self.root_collection = self.get(self.base + '/.well-known/collection') self.subcollections = self.get(self.root_collection['response'] @@ -21,8 +23,8 @@ def __init__(self): self.fix_discovery() def update_collection(self, collection): - response = self.get(self.collections[collection]['url'])['response'] - self.collections[collection]['response'] = response + response = self.get(self.collections[collection]['url'])['response'] + self.collections[collection]['response'] = response def fix_discovery(self): """The Hypermedia items are semi-permanent. Some things change @@ -34,7 +36,7 @@ def fix_discovery(self): if item['id'] == 'LDSO': try: self.update_collection("LDSO") - except: + except KeyError: self.lds_user = False else: self.lds_user = True diff --git a/familysearch/discussions.py b/familysearch/discussions.py index db484e2..2f9283f 100644 --- a/familysearch/discussions.py +++ b/familysearch/discussions.py @@ -9,10 +9,10 @@ class Discussions: """https://familysearch.org/developers/docs/api/resources#discussions""" + def __init__(self): """https://familysearch.org/developers/docs/api/resources#discussions""" self.discuss_base = self.base + "/platform/discussions/discussions/" - pass def discussions(self): """https://familysearch.org/developers/docs/api/discussions/Discussions_resource""" diff --git a/familysearch/memories.py b/familysearch/memories.py index a4b80c6..b23e74a 100644 --- a/familysearch/memories.py +++ b/familysearch/memories.py @@ -9,36 +9,45 @@ class Memories: """https://familysearch.org/developers/docs/api/resources#memories""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#memories""" + self.memory_base = self.tree_base + "/platform/memories/memories/" def memories(self, **kwargs): """https://familysearch.org/developers/docs/api/memories/Memories_resource""" + return self._add_query_params(self.memory_base[::-1], kwargs) def memory(self, mid): """https://familysearch.org/developers/docs/api/memories/Memory_resource""" + return self.memory_base + mid def user_memories(self, **kwargs): """https://familysearch.org/developers/docs/api/memories/User_Memories_resource""" + return self._add_query_params( self.memory_base + "users/" + self.user['userId'] + "/memories", kwargs) def memory_personas(self, mid): """https://familysearch.org/developers/docs/api/memories/Memory_Personas_resource""" + return self.memory_base + mid + "personas" def memory_persona(self, mid, pid): """https://familysearch.org/developers/docs/api/memories/Memory_Persona_resource""" + return self.memory_base + mid + "personas/" + pid def memory_comments(self, mid): """https://familysearch.org/developers/docs/api/memories/Memory_Comments_resource""" + return self.memory_base + mid + "comments" def memories_comment(self, mid, cmid): """https://familysearch.org/developers/docs/api/memories/Memories_Comment_resource""" + return self.memory_base + mid + "comments/" + cmid diff --git a/familysearch/ordinances.py b/familysearch/ordinances.py index 8419708..4513a59 100644 --- a/familysearch/ordinances.py +++ b/familysearch/ordinances.py @@ -9,6 +9,8 @@ class Ordinances: """https://familysearch.org/developers/docs/api/resources#ordinances""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#ordinances""" + pass # Placeholder file. diff --git a/familysearch/parentsAndChildren.py b/familysearch/parentsAndChildren.py index 95b98d4..f7de681 100644 --- a/familysearch/parentsAndChildren.py +++ b/familysearch/parentsAndChildren.py @@ -9,31 +9,40 @@ class ParentsAndChildren: """https://familysearch.org/developers/docs/api/resources#parents-and-children""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#parents-and-children""" + self.child_base = self.tree_base + "child-and-parents-relationships/" def child_relationship(self, crid): """https://familysearch.org/developers/docs/api/tree/Child-and-Parents_Relationship_resource""" + return self.child_base + crid def child_relationship_parent(self, crid, role): """https://familysearch.org/developers/docs/api/tree/Child-and-Parents_Relationship_Parent_resource""" + return self.child_base + crid + "/" + role def child_relationship_conclusion(self, crid, role, cid): """https://familysearch.org/developers/docs/api/tree/Child-and-Parents_Relationship_Conclusion_resource""" + return self.delete( self.child_base + crid + "/" + role + "/conclusions/" + cid) def child_relationship_notes(self, crid): """https://familysearch.org/developers/docs/api/tree/Child-and-Parents_Relationship_Notes_resource""" + + return self.child_base + crid + "/notes" def child_relationship_note(self, crid, nid): """https://familysearch.org/developers/docs/api/tree/Child-and-Parents_Relationship_Note_resource""" + return self.child_base + crid + "/notes/" + nid def child_relationship_restore(self, crid): """https://familysearch.org/developers/docs/api/tree/Child-and-Parents_Relationship_Restore_resource""" + return self.child_base + crid + "/restore" diff --git a/familysearch/pedigree.py b/familysearch/pedigree.py index f481015..965f292 100644 --- a/familysearch/pedigree.py +++ b/familysearch/pedigree.py @@ -9,16 +9,20 @@ class Pedigree: """https://familysearch.org/developers/docs/api/examples#pedigree""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#pedigree""" + pass def ancestry(self, pid, **kwargs): """https://familysearch.org/developers/docs/api/tree/Ancestry_resource""" + return self._add_query_params( self.tree_base + 'ancestry?person=' + pid, kwargs) def descendancy(self, pid, **kwargs): """https://familysearch.org/developers/docs/api/tree/Descendancy_resource""" + return self._add_query_params( self.tree_base + 'descendancy?person=' + pid, kwargs) diff --git a/familysearch/person.py b/familysearch/person.py index 930fcfb..1ded840 100644 --- a/familysearch/person.py +++ b/familysearch/person.py @@ -9,124 +9,153 @@ class Person: """https://familysearch.org/developers/docs/api/examples#person""" + def __init__(self): """https://familysearch.org/developers/docs/api/resources#person""" + self.person_base = self.tree_base + 'persons/' def persons(self): """https://familysearch.org/developers/docs/api/tree/Persons_resource""" + return self.person_base[::-1] def person(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_resource""" + return self.person_base + pid def person_parents(self, pid): """https://familysearch.org/developers/docs/api/tree/Parents_of_a_Person_resource""" + return self.person_base + pid + "/parents" def person_spouses(self, pid): """https://familysearch.org/developers/docs/api/tree/Spouses_of_a_Person_resource""" + return self.person_base + pid + "/spouses" def person_children(self, pid): """https://familysearch.org/developers/docs/api/tree/Children_of_a_Person_resource""" + return self.get(self.person_base + pid + "/children") def spouse_relationships(self, pid, **kwargs): """https://familysearch.org/developers/docs/api/tree/Person_Relationships_to_Spouses_resource""" + return self._add_query_params( self.person_base + pid + '/spouse-relationships', kwargs) def child_relationships(self, pid, **kwargs): """https://familysearch.org/developers/docs/api/tree/Relationships_to_Children_resource""" + return self._add_query_params( self.person_base + pid + '/child-relationships', kwargs) def parent_relationships(self, pid, **kwargs): """https://familysearch.org/developers/docs/api/tree/Relationships_to_Parents_resource""" + return self._add_query_params( self.person_base + pid + '/parent_relationships', kwargs) def person_with_relationships(self, **kwargs): """https://familysearch.org/developers/docs/api/tree/Person_With_Relationships_resource""" + return self._add_query_params( self.tree_base + "persons-with-relationships", kwargs) def person_conclusion(self, pid, cid): """https://familysearch.org/developers/docs/api/tree/Person_Conclusion_resource""" + return self.person_base + pid + "conclusions" + cid def person_source_references(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Source_References_resource""" + return self.person_base + pid + "source-references" def person_source_reference(self, pid, srid): """https://familysearch.org/developers/docs/api/tree/Person_Source_References_resource""" + return self.person_base + pid + "source-references/" + srid def person_sources_query(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Sources_Query_resource""" + return self.person_base + pid + "sources" def person_note(self, pid, nid): """https://familysearch.org/developers/docs/api/tree/Person_Note_resource""" + return self.person_base + pid + "notes" + nid def person_discussion_references(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Discussion_References_resource""" + return self.person_base + pid + "/discussion_reference" def person_discussion_reference(self, pid, drid): """https://familysearch.org/developers/docs/api/tree/Person_Discussion_References_resource""" + return self.person_base + pid + "/discussion_reference/" + drid def person_merge(self, pid, dpid, **kwargs): """https://familysearch.org/developers/docs/api/tree/Person_Merge_resource""" + return self._add_query_params( self.person_base + pid + "/merges/" + dpid, kwargs) def person_change_summary(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Merge_resource""" + return self.person_base + pid + "/change-summary" def person_not_a_match(self, pid, dpid): """https://familysearch.org/developers/docs/api/tree/Person_Not_A_Match_resource""" + return self.person_base + pid + "/not-a-match/" + dpid def person_restore(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Restore_resource""" + return self.person_base + pid + "/restore" def preferred_spouse_relationship(self, pid, uid=None): """https://familysearch.org/developers/docs/api/tree/Preferred_Spouse_Relationship_resource""" + if uid is None: uid = self.user['userId'] return self.user_base + uid + "/preferred-spouse-relationships/" + pid def preferred_parent_relationship(self, pid, uid=None): """https://familysearch.org/developers/docs/api/tree/Preferred_Parent_Relationship_resource""" + if uid is None: uid = self.user['userId'] return self.user_base + uid + "/preferred-parent-relationships/" + pid - def person_memories(self, pid): + def person_memories(self, pid, **kwargs): """https://familysearch.org/developers/docs/api/tree/Person_Memories_resource""" + return self._add_query_params( self.person_base + pid + "/memories", kwargs) def person_memory_references(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Memory_References_resource""" + return self.person_base + pid + "/memory-references" def person_memory_reference(self, pid, erid): """https://familysearch.org/developers/docs/api/tree/Person_Memory_References_resource""" + return self.person_base + pid + "/memory-references/" + erid def person_memories_portrait(self, pid, **kwargs): """https://familysearch.org/developers/docs/api/tree/Person_Memories_Portrait_resource""" + return self._add_query_params(self.person_base + pid + "/portrait", kwargs) def person_portraits(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Portraits_resource""" + return self.person_base + "/portrait" diff --git a/familysearch/places.py b/familysearch/places.py index 31025f2..c08e75c 100644 --- a/familysearch/places.py +++ b/familysearch/places.py @@ -9,24 +9,30 @@ class Places: """https://familysearch.org/developers/docs/api/resources#places""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#places""" + self.places_base = self.base + "/platform/places/" def places_search(self, **kwargs): """https://familysearch.org/developers/docs/api/places/Places_Search_resource""" + return self._add_query_params(self.places_base + "search", kwargs) def place_description(self, pdid): """https://familysearch.org/developers/docs/api/places/Place_Description_resource""" + return self.places_base + "description/" + pdid def place_group(self, pgid): """https://familysearch.org/developers/docs/api/places/Place_Group_resource""" + return self.places_base + "groups/" + pgid def place(self, pid): """https://familysearch.org/developers/docs/api/places/Place_resource""" + return self.places_base + pid def place_description_children(self): @@ -35,16 +41,20 @@ def place_description_children(self): def place_type(self, ptid): """https://familysearch.org/developers/docs/api/places/Place_Type_resource""" + return self.places_base + "types/" + ptid def place_type_group(self, ptgid): """https://familysearch.org/developers/docs/api/places/Place_Type_Group_resource""" + return self.places_base + "type-groups/" + ptgid def place_types(self): """https://familysearch.org/developers/docs/api/places/Place_Types_resource""" + return self.places_base + "types" def place_type_groups(self): """https://familysearch.org/developers/docs/api/places/Place_Type_Groups_resource""" + return self.places_base + "type-groups" diff --git a/familysearch/records.py b/familysearch/records.py index b95455e..d22301c 100644 --- a/familysearch/records.py +++ b/familysearch/records.py @@ -9,8 +9,10 @@ class Records: """https://familysearch.org/developers/docs/api/resources#records""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#records""" + pass # There might be a way to reverse-engineer something from the resource, diff --git a/familysearch/searchAndMatch.py b/familysearch/searchAndMatch.py index 58942a1..f9309cd 100644 --- a/familysearch/searchAndMatch.py +++ b/familysearch/searchAndMatch.py @@ -9,22 +9,29 @@ class SearchAndMatch: """https://familysearch.org/developers/docs/api/resources#search-and-match""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#search-and-match""" + pass + def person_search(self, **kwargs): """https://familysearch.org/developers/docs/api/tree/Person_Search_resource""" + return self._add_query_params(self.tree_base + "search", kwargs) def person_matches(self, pid, **kwargs): """https://familysearch.org/developers/docs/api/tree/Person_Matches_resource""" + return self._add_query_params( self.tree_base + "persons/" + pid + "/matches", kwargs) def person_not_a_match_list(self, pid): """https://familysearch.org/developers/docs/api/tree/Person_Not_A_Match_List_resource""" + return self.tree_base + "persons/" + pid + "/not-a-match" def person_matches_query(self, **kwargs): """https://familysearch.org/developers/docs/api/tree/Person_Matches_Query_resource""" + return self._add_query_params(self.tree_base + "matches", kwargs) diff --git a/familysearch/sources.py b/familysearch/sources.py index 363036d..601c6c6 100644 --- a/familysearch/sources.py +++ b/familysearch/sources.py @@ -9,41 +9,51 @@ class Sources: """https://familysearch.org/developers/docs/api/resources#sources""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#sources""" + self.source_base = self.base + "/platform/sources/" def source_descriptions(self): """https://familysearch.org/developers/docs/api/sources/Source_Descriptions_resource""" + return self.source_base + "descriptions" def source_description(self, sdid): """https://familysearch.org/developers/docs/api/sources/Source_Description_resource""" + return self.source_base + "descriptions/" + sdid def source_folders(self): """https://familysearch.org/developers/docs/api/sources/Source_Folders_resource""" + return self.source_base + "collections" def source_folder(self, udcid): """https://familysearch.org/developers/docs/api/sources/Source_Folder_resource""" + return self.source_base + "collections/" + udcid def source_folder_source_descriptions(self, udcid, **kwargs): """https://familysearch.org/developers/docs/api/sources/Source_Folder_Source_Descriptions_resource""" + return self._add_query_params( self.source_base + "collections/" + udcid + "/descriptions", kwargs) def user_source_folders(self): """https://familysearch.org/developers/docs/api/sources/User_Source_Folders_resource""" + return self.source_base + self.user['personId'] + "/collections" def user_source_descriptions(self, **kwargs): """https://familysearch.org/developers/docs/api/sources/User_Source_Descriptions_resource""" + return self._add_query_params( self.source_base + self.user['personId'] + "/collections", kwargs) def source_references_query(self, **kwargs): """https://familysearch.org/developers/docs/api/tree/Source_References_Query_resource""" + return self._add_query_params( self.tree_base + "source_references", kwargs) diff --git a/familysearch/spouses.py b/familysearch/spouses.py index ea2ab11..73ad249 100644 --- a/familysearch/spouses.py +++ b/familysearch/spouses.py @@ -9,30 +9,38 @@ class Spouses: """https://familysearch.org/developers/docs/api/resources#spouses""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#spouses""" + self.couple_base = self.tree_base + 'couple-relationships/' def relationship(self): """https://familysearch.org/developers/docs/api/tree/Relationships_resource""" + return self.tree_base + "relationships" def couple_relationship(self, cpid, **kwargs): """https://familysearch.org/developers/docs/api/tree/Couple_Relationship_resource""" + return self._add_query_params(self.couple_base + cpid, kwargs) def couple_relationship_conclusion(self, cpid, cid): """https://familysearch.org/developers/docs/api/tree/Couple_Relationship_Conclusion_resource""" + return self.couple_base + cpid + '/conclusions/' + cid def couple_relationship_notes(self, crid): """https://familysearch.org/developers/docs/api/tree/Couple_Relationship_Notes_resource""" + return self.couple_base + crid + "/notes" def couple_relationship_note(self, crid): """https://familysearch.org/developers/docs/api/tree/Couple_Relationship_Note_resource""" + return self.couple_base + crid + "/notes/" + nid def couple_relationship_restore(self, cpid): """https://familysearch.org/developers/docs/api/tree/Couple_Relationship_Restore_resource""" + return self.couple_base + cpid + '/restore' diff --git a/familysearch/user.py b/familysearch/user.py index 492ebad..cdde510 100644 --- a/familysearch/user.py +++ b/familysearch/user.py @@ -9,18 +9,21 @@ class User(object): """https://familysearch.org/developers/docs/api/resources#user""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#user""" pass def current_user(self): """https://familysearch.org/developers/docs/api/users/Current_User_resource""" + url = self.root_collection['response']['collections'][0]['links']\ ['current-user']['href'] return url def current_user_person(self): """https://familysearch.org/developers/docs/api/tree/Current_Tree_Person_resource""" + try: url = self.collections["FSFT"]["response"]["collections"][0][ "links"]["current-user-person"]["href"] @@ -32,10 +35,12 @@ def current_user_person(self): def agent(self, uid): """https://familysearch.org/developers/docs/api/users/Agent_resource""" + return self.user_base + "agents/" + uid def current_user_history(self): """https://familysearch.org/developers/docs/api/users/Current_User_History_resource""" + try: url = self.collections["FSFT"]["response"]["collections"][0][ "links"]["current-user-history"]["href"] diff --git a/familysearch/utilities.py b/familysearch/utilities.py index 172350b..9537671 100644 --- a/familysearch/utilities.py +++ b/familysearch/utilities.py @@ -9,21 +9,26 @@ class Utilities: """https://familysearch.org/developers/docs/api/resources#utilities""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#utilities""" + pass def pending_modifications(self): """https://familysearch.org/developers/docs/api/tree/Pending_Modifications_resource""" + return self.root_collection['response']['collections'][0]['links']\ ['pending-modifications']['href'] def redirect(self, **kwargs): """https://familysearch.org/developers/docs/api/tree/Redirect_resource""" + return self._add_query_params( self.base + "/platform/redirect", kwargs) def oembed(self, **kwargs): """https://familysearch.org/developers/docs/api/discovery/OEmbed_resource""" + return self._add_query_params( self.base + "/platform/oembed", kwargs) diff --git a/familysearch/vocabularies.py b/familysearch/vocabularies.py index f6266e2..420abf5 100644 --- a/familysearch/vocabularies.py +++ b/familysearch/vocabularies.py @@ -9,12 +9,15 @@ class Vocabularies: """https://familysearch.org/developers/docs/api/resources#vocabularies""" + def __init__(self): """https://familysearch.org/developers/docs/api/examples#vocabularies""" + self.vocab_base = self.base + "platform/vocab/" def vocabulary_list(self, cvlid): """https://familysearch.org/developers/docs/api/cv/Controlled_Vocabulary_List_resource""" + try: url = self.collections["FSCV"]["response"]['collections'][ 0]['links']['vocab-list']['template'] @@ -29,6 +32,7 @@ def vocabulary_list(self, cvlid): def vocabulary_lists(self): """https://familysearch.org/developers/docs/api/cv/Controlled_Vocabulary_List_resource""" + try: url = self.collections["FSCV"]["response"]['collections'][ 0]['links']['vocab-lists']['href'] @@ -40,8 +44,7 @@ def vocabulary_lists(self): def vocabulary_term(self, cvtid): """https://familysearch.org/developers/docs/api/cv/Controlled_Vocabulary_Term_resource""" - # return self.vocab_base + "lists/" + cvtid - """https://familysearch.org/developers/docs/api/cv/Controlled_Vocabulary_List_resource""" + try: url = self.collections["FSCV"]["response"]['collections'][ 0]['links']['vocab-term']['template'] From dc552e62abaa2e1182c317c6ae6313a3d4bfd90b Mon Sep 17 00:00:00 2001 From: Doug Henderson Date: Mon, 23 May 2016 23:21:58 -0600 Subject: [PATCH 12/14] make login work --- sample-apps/web_person/web-person.py | 64 ++++++++++++++------- sample-apps/whoami/whoami.py | 86 +++++++++++++++------------- 2 files changed, 91 insertions(+), 59 deletions(-) diff --git a/sample-apps/web_person/web-person.py b/sample-apps/web_person/web-person.py index ed07db8..0b8650b 100644 --- a/sample-apps/web_person/web-person.py +++ b/sample-apps/web_person/web-person.py @@ -23,16 +23,10 @@ config = configparser.ConfigParser() config.read(config_path) -try: - app_key = config["fskey"]["devkey"] - base = config["fskey"]["base"] - port = config["server"]["port"] - redirect = config["server"]["redirect_uri"] -except AttributeError: - app_key = config.get("fskey", "devkey") - base = config.get("fskey", "base") - port = config.get("server", "port") - redirect = config.get("server", "redirect_uri") +dev_key = config.get("fskey", "devkey") +base = config.get("fskey", "base") +port = config.get("server", "port") +redirect = config.get("server", "redirect_uri") url = "http://localhost" + (":" + port) if port is not "80" else "" ruri = "" @@ -41,7 +35,7 @@ if x is "/": break -fs = FamilySearch("FSPySDK/SampleApps", app_key, base=base) +fs = FamilySearch("FSPySDK/SampleApps", dev_key, base=base) try: fslogin = fs.root_collection["response"]['collections'][0]['links']\ @@ -76,39 +70,44 @@ def hr(): print("="*80) print("""fs.root_collection["response"]["collections"][0]["links"]['http://oauth.net/core/2.0/endpoint/authorize']['href']: ...""") pp.pprint(fs.root_collection["response"]["collections"][0]["links"]['http://oauth.net/core/2.0/endpoint/authorize']['href']) hr() -qshow() +# qshow() +print("fslogin:", fslogin) fslogin = fs._add_query_params(fslogin, { 'response_type': 'code', - 'client_id': fs.key, + 'client_id': fs.dev_key, 'redirect_uri': redirect }) +print("fslogin:", fslogin) class getter(server.BaseHTTPRequestHandler): + def do_GET(self): self.send_response(code=200) - self.send_header("Content-type", "text/html") + self.send_header("Content-type", "text/html;charset=utf-8") self.end_headers() path = self.path + print("path:", path) top = "FSPySDK Sample App" + top += '' middle = "" bottom = "" if path.startswith(ruri): bottom = self.get_code(path) + bottom else: if fs.logged_in: - middle += logged_in() + middle += self.logged_in() if path.startswith("/?pid="): pid = parse_qs(path)["/?pid"][0] - person = fs.get_person(pid) - middle += has_pid(person) + person = fs.get(fs.person(pid)) # = fs.get_person(pid) + middle += self.has_pid(person) else: middle = self.not_logged_in() + middle middle = self.not_logged_in() - body = top + middle + bottom + body = top + middle + bottom self.wfile.write(body.encode("utf-8")) @@ -120,9 +119,34 @@ def not_logged_in(self): return string def logged_in(self): + def show_fs(): + print("="*80) + pp = pprint.PrettyPrinter(width=120, indent=2) + for k in sorted(list(fs.__dict__.keys())): + if k[0] == '_': continue + v = fs.__dict__[k] + vt = type(v) + if isinstance(v, (bool, type(''), type(b''), type(u''), tuple, list, set, frozenset)): + print(k, vt, v) + elif isinstance(v, (dict,)): + print(k, vt, "...") + pp.pprint(v) + else: + print(k, vt) + print("") + print("="*80) + + def p(): + show_fs() + print("fs.user:", type(fs.user), fs.user) + print("fs.current_user():", fs.current_user()) + print("fs.current_user_person():", fs.current_user_person()) + print("fs.agent('x'):", fs.agent('x')) + print("fs.current_user_history():", fs.current_user_history()) + p() string = 'Search given FamilySearch PID (default is your own)' - string += ' Date: Mon, 23 May 2016 23:24:31 -0600 Subject: [PATCH 13/14] fix accept headers --- test/test_basefs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_basefs.py b/test/test_basefs.py index 204afd9..b641b14 100644 --- a/test/test_basefs.py +++ b/test/test_basefs.py @@ -40,7 +40,7 @@ def test_base_fs_creation(self): fs = familysearch.FamilySearch(self.agent, self.devkey) self.assertTrue(fs.base == 'https://sandbox.familysearch.org') print("Base is correct.") - self.assertTrue(fs.key == self.devkey) + self.assertTrue(fs.dev_key == self.devkey) print("Key is correct.") self.assertTrue(isinstance(fs.opener, request.OpenerDirector)) print("HTTP opener works.") From 95f14e549a3cab5955f0f91998837b40cc2f846c Mon Sep 17 00:00:00 2001 From: "Travis E. Oliphant" Date: Thu, 29 Jun 2017 20:58:19 -0500 Subject: [PATCH 14/14] Fix use of super for Python 2 --- familysearch/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/familysearch/__init__.py b/familysearch/__init__.py index 9ab83d4..9fe7f3a 100644 --- a/familysearch/__init__.py +++ b/familysearch/__init__.py @@ -31,12 +31,14 @@ from urllib.request import build_opener from urllib.error import HTTPError from urllib.parse import(urlsplit, urlunsplit, parse_qs, urlencode) + PY3 = True except ImportError: # Python 2 from urllib import urlencode from urllib2 import Request as BaseRequest from urllib2 import build_opener, HTTPError from urlparse import(urlsplit, urlunsplit, parse_qs) + PY3 = False import json import time @@ -120,7 +122,10 @@ def __init__(self, agent, key, session=None, self.logged_in = bool(self.access_token) Discovery.__init__(self) - super().__init__() + if PY3: + super().__init__() + else: + super(FamilySearch, self).__init__() # Discovery needs to explicitly be first, as it is the hypermedia # engine.