hakai_api 1.5.2__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hakai_api/__init__.py +23 -2
- hakai_api/auth/__init__.py +7 -0
- hakai_api/auth/base.py +256 -0
- hakai_api/auth/desktop.py +263 -0
- hakai_api/auth/desktop_callback.html +94 -0
- hakai_api/auth/web.py +94 -0
- hakai_api/client.py +390 -0
- {hakai_api-1.5.2.dist-info → hakai_api-2.0.0.dist-info}/METADATA +90 -11
- hakai_api-2.0.0.dist-info/RECORD +11 -0
- {hakai_api-1.5.2.dist-info → hakai_api-2.0.0.dist-info}/licenses/LICENSE +1 -1
- hakai_api/Client.py +0 -168
- hakai_api-1.5.2.dist-info/RECORD +0 -6
- {hakai_api-1.5.2.dist-info → hakai_api-2.0.0.dist-info}/WHEEL +0 -0
hakai_api/Client.py
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
"""Get authorized requests to the Hakai API using the requests library.
|
|
2
|
-
|
|
3
|
-
Written by: Taylor Denouden, Chris Davis, and Nate Rosenstock
|
|
4
|
-
Last updated: April 2021
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import json
|
|
8
|
-
import os
|
|
9
|
-
from datetime import datetime
|
|
10
|
-
from time import mktime
|
|
11
|
-
from typing import Dict, Union
|
|
12
|
-
|
|
13
|
-
from requests_oauthlib import OAuth2Session
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class Client(OAuth2Session):
|
|
17
|
-
_credentials_file = os.path.expanduser("~/.hakai-api-auth")
|
|
18
|
-
DEFAULT_API_ROOT = "https://hecate.hakai.org/api"
|
|
19
|
-
DEFAULT_LOGIN_PAGE = "https://hecate.hakai.org/api-client-login"
|
|
20
|
-
CREDENTIALS_ENV_VAR = "HAKAI_API_CREDENTIALS"
|
|
21
|
-
USER_AGENT_ENV_VAR = "HAKAI_API_USER_AGENT"
|
|
22
|
-
|
|
23
|
-
def __init__(
|
|
24
|
-
self,
|
|
25
|
-
api_root: str = DEFAULT_API_ROOT,
|
|
26
|
-
login_page: str = DEFAULT_LOGIN_PAGE,
|
|
27
|
-
credentials: Union[str, Dict] = None,
|
|
28
|
-
):
|
|
29
|
-
"""Create a new Client class with credentials.
|
|
30
|
-
|
|
31
|
-
Params:
|
|
32
|
-
api_root: The base url of the hakai api you want to call.
|
|
33
|
-
Defaults to the production server.
|
|
34
|
-
login_page: The url of the login page to direct users to.
|
|
35
|
-
Defaults to the production login page.
|
|
36
|
-
credentials (str, Dict): Credentials token retrieved from the hakai api
|
|
37
|
-
login page. If `None`, loads cached credentials or prompts for log in.
|
|
38
|
-
"""
|
|
39
|
-
self._api_root = api_root
|
|
40
|
-
self._login_page = login_page
|
|
41
|
-
self._credentials = None
|
|
42
|
-
|
|
43
|
-
env_credentials = os.getenv(self.CREDENTIALS_ENV_VAR, None)
|
|
44
|
-
if isinstance(credentials, dict):
|
|
45
|
-
self._credentials = credentials
|
|
46
|
-
elif isinstance(credentials, str):
|
|
47
|
-
# Parse credentials from string
|
|
48
|
-
self._credentials = self._parse_credentials_string(credentials)
|
|
49
|
-
elif env_credentials is not None:
|
|
50
|
-
self._credentials = self._parse_credentials_string(env_credentials)
|
|
51
|
-
elif self.file_credentials_are_valid():
|
|
52
|
-
self._credentials = self._get_credentials_from_file()
|
|
53
|
-
else:
|
|
54
|
-
self._credentials = self._get_credentials_from_web()
|
|
55
|
-
|
|
56
|
-
if self._credentials is None:
|
|
57
|
-
raise ValueError("Credentials could not be set.")
|
|
58
|
-
|
|
59
|
-
# Cache the credentials
|
|
60
|
-
self._save_credentials_to_file(self._credentials)
|
|
61
|
-
|
|
62
|
-
# Init the OAuth2Session parent class with credentials
|
|
63
|
-
super(Client, self).__init__(token=self._credentials)
|
|
64
|
-
|
|
65
|
-
# Set User-Agent header
|
|
66
|
-
user_agent = os.getenv(self.USER_AGENT_ENV_VAR, "hakai-api-client-py")
|
|
67
|
-
self.headers.update({"User-Agent": user_agent})
|
|
68
|
-
|
|
69
|
-
@property
|
|
70
|
-
def api_root(self) -> str:
|
|
71
|
-
"""Return the api base url."""
|
|
72
|
-
return self._api_root
|
|
73
|
-
|
|
74
|
-
@property
|
|
75
|
-
def login_page(self) -> str:
|
|
76
|
-
"""Return the login page url."""
|
|
77
|
-
return self._login_page
|
|
78
|
-
|
|
79
|
-
@property
|
|
80
|
-
def credentials(self) -> Dict:
|
|
81
|
-
"""Return the credentials object."""
|
|
82
|
-
if self._credentials is None:
|
|
83
|
-
raise ValueError("Credentials have not been set.")
|
|
84
|
-
return self._credentials
|
|
85
|
-
|
|
86
|
-
@classmethod
|
|
87
|
-
def reset_credentials(cls):
|
|
88
|
-
"""Remove the cached credentials file."""
|
|
89
|
-
if os.path.isfile(cls._credentials_file):
|
|
90
|
-
os.remove(cls._credentials_file)
|
|
91
|
-
|
|
92
|
-
def _save_credentials_to_file(self, credentials: Dict):
|
|
93
|
-
"""Save the credentials object to a file."""
|
|
94
|
-
with open(self._credentials_file, "w") as outfile:
|
|
95
|
-
json.dump(credentials, outfile)
|
|
96
|
-
|
|
97
|
-
@classmethod
|
|
98
|
-
def file_credentials_are_valid(cls) -> bool:
|
|
99
|
-
"""Check if the cached credentials exist and are valid."""
|
|
100
|
-
if not os.path.isfile(cls._credentials_file):
|
|
101
|
-
return False
|
|
102
|
-
with open(cls._credentials_file, "r"):
|
|
103
|
-
try:
|
|
104
|
-
credentials = cls._get_credentials_from_file()
|
|
105
|
-
expires_at = credentials["expires_at"]
|
|
106
|
-
except (KeyError, ValueError):
|
|
107
|
-
os.remove(cls._credentials_file)
|
|
108
|
-
return False
|
|
109
|
-
|
|
110
|
-
now = int(
|
|
111
|
-
(
|
|
112
|
-
mktime(datetime.now().timetuple())
|
|
113
|
-
+ datetime.now().microsecond / 1000000.0
|
|
114
|
-
)
|
|
115
|
-
) # utc timestamp
|
|
116
|
-
|
|
117
|
-
if now > expires_at:
|
|
118
|
-
cls.reset_credentials()
|
|
119
|
-
return False
|
|
120
|
-
|
|
121
|
-
return True
|
|
122
|
-
|
|
123
|
-
@classmethod
|
|
124
|
-
def _get_credentials_from_file(cls) -> Dict:
|
|
125
|
-
"""Get user credentials from a cached file."""
|
|
126
|
-
with open(cls._credentials_file, "r") as infile:
|
|
127
|
-
result = json.load(infile)
|
|
128
|
-
result = Client._check_keys_convert_types(result)
|
|
129
|
-
return result
|
|
130
|
-
|
|
131
|
-
def _get_credentials_from_web(self) -> Dict:
|
|
132
|
-
"""Get user credentials from a web sign-in."""
|
|
133
|
-
print("Please go here and authorize:")
|
|
134
|
-
print(self.login_page, flush=True)
|
|
135
|
-
response = input("\nCopy and past your credentials from the login page:\n")
|
|
136
|
-
|
|
137
|
-
# Reformat response to dict
|
|
138
|
-
credentials = dict(map(lambda x: x.split("="), response.split("&")))
|
|
139
|
-
return credentials
|
|
140
|
-
|
|
141
|
-
@staticmethod
|
|
142
|
-
def _parse_credentials_string(credentials: str) -> Dict:
|
|
143
|
-
"""Parse a credentials string into a dictionary."""
|
|
144
|
-
result = dict(map(lambda x: x.split("="), credentials.split("&")))
|
|
145
|
-
result = Client._check_keys_convert_types(result)
|
|
146
|
-
return result
|
|
147
|
-
|
|
148
|
-
@staticmethod
|
|
149
|
-
def _check_keys_convert_types(credentials: dict) -> dict:
|
|
150
|
-
"""Check that the credentials dict has the required keys and convert types."""
|
|
151
|
-
missing_keys = [
|
|
152
|
-
key
|
|
153
|
-
for key in ["access_token", "token_type", "expires_at"]
|
|
154
|
-
if key not in credentials
|
|
155
|
-
]
|
|
156
|
-
if len(missing_keys) > 0:
|
|
157
|
-
raise ValueError(
|
|
158
|
-
f"Credentials string is missing required keys: {str(missing_keys)}."
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
# Convert expires_at to int
|
|
162
|
-
credentials["expires_at"] = int(float(credentials["expires_at"]))
|
|
163
|
-
|
|
164
|
-
# If expires_in is present, convert to int
|
|
165
|
-
if "expires_in" in credentials:
|
|
166
|
-
credentials["expires_in"] = int(float(credentials["expires_in"]))
|
|
167
|
-
|
|
168
|
-
return credentials
|
hakai_api-1.5.2.dist-info/RECORD
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
hakai_api/Client.py,sha256=L4K-Hhb-IGZJ3wydi7hMEdENM_h7duPaL4RtsACCQvw,6062
|
|
2
|
-
hakai_api/__init__.py,sha256=6jSdZtw1AZRFEsPxiYB7jpQ1B_xlBCho6IPZdGarOxA,56
|
|
3
|
-
hakai_api-1.5.2.dist-info/METADATA,sha256=e219ERceF9DFeR_eWzxmCB9vudXSGviKAXIr8PCJFA4,4023
|
|
4
|
-
hakai_api-1.5.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
5
|
-
hakai_api-1.5.2.dist-info/licenses/LICENSE,sha256=Ag7Q1MVBG7kruFExfcgV9OajxdpvGDbo0n4lft2yMWs,1072
|
|
6
|
-
hakai_api-1.5.2.dist-info/RECORD,,
|
|
File without changes
|