-
Notifications
You must be signed in to change notification settings - Fork 0
/
hypy.py
243 lines (214 loc) · 8.67 KB
/
hypy.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# -*- coding: utf-8 -*-
import requests
def debug_print(func_name,response):
"""Prints useful information for each request from its response"""
print(f"<{func_name}> {response.request.method} URL: {response.url}")
class HypothesisAPI(object):
url: None
user: None
name: None
def __init__(self, developerAPIKey=None, authority="hypothes.is"):
self._ss = requests.Session()
self._ss.headers.update({'Accept': 'application/json'})
self.authority = authority
self.developerAPIKey = developerAPIKey
profile = self.profile()
try:
self.user = profile["userid"].replace("acct:","").replace("@"+self.authority,"")
self.name = profile["user_info"]["display_name"]
except AttributeError: #e.g. if userid = None
print("Authentication error, no user found for developerAPIKey.")
@property
def authority(self):
return self._authority
@authority.setter
def authority(self, authority):
self._authority = authority
self.url = f"https://{self.authority}/api"
@property
def developerAPIKey(self):
return self._developerAPIKey
@developerAPIKey.setter
def developerAPIKey(self,developerAPIKey):
self._developerAPIKey = developerAPIKey
auth_header = None if developerAPIKey is None else 'Bearer '+developerAPIKey
self._ss.headers.update({'Authorization': auth_header}) #removes header key if value is None
def url_for(self, *args):
"""URL helper to form full URL for methods."""
request_url = self.url
for arg in args:
request_url = "/".join([request_url,arg])
return request_url
"""
API methods
"""
def profile(self): #Responses: 200
"""Return profile of authenticated user.
This can either be accessed with an API key, in which case it will show the
authenticated user's profile, or without, in which case it will show infor-
mation for a logged-out profile, including only public groups for the cur-
rent authority.
Args:
None
Returns:
json response from hypothes.is
Examples:
>>> h = HypothesisAPI(developerAPIKey)
>>> p = h.profile
>>> print p['userid']
"""
response = self._ss.get(self.url_for("profile"))
return response.json()
def get_links(self):
"""URL templates for generating URLs for HTML pages."""
response = self._ss.get(self.url_for("links"))
debug_print(self.__class__.__name__,response)
return response.json()
class AnnotationAPI(HypothesisAPI):
def __init__(self, developerAPIKey=None, authority="hypothes.is"):
super().__init__(developerAPIKey, authority)
def get(self, id: str): #Responses: 200,404
"""
Fetch an annotation.
Keyword arguments:
id -- <str> ID of annotation to return.
"""
try:
response = self._ss.get(self.url_for("annotations",id))
debug_print(self.__class__.__name__,response)
response.raise_for_status()
except requests.exceptions.HTTPError as err:
print(err)
else:
return response.json()
def search(self, **kwargs): #Responses: 200
"""
Search for annotations.
Keyword arguments:
limit=<int> [0 .. 200](default 20)
The maximum number of annotations to return.
offset=<int> [0 .. 9800](default 0)
The number of initial annotations to skip.
This is used for pagination. Not suitable for paging through thousands
of annotations-search_after should be used instead.
sort=<str> [Enum:"created" "updated" "group" "id" "user" ](default "updated")
The field by which annotations should be sorted.
search_after=<str>
Returns results after the annotation whose sort field
has this value. If specifying a date use the format
yyyy-MM-dd'T'HH:mm:ss.SSX or time in miliseconds since the epoch.
This is used for iteration through large collections of results.
order=<str> [Enum:"asc" "desc"](default "desc")
The order in which the results should be sorted.
uri=<str>
Limit the results to annotations matching the specific URI or equivalent URIs.
URI can be a URL (a web page address) or a URN representing another kind of
resource such as DOI (Digital Object Identifier) or a PDF fingerprint.
uri.parts=<str>
Limit the results to annotations containing the given keyword in the URL.
url=<str>
Alias of `uri`.
wildcard_uri=<str>
Limit the results to annotations matching the wildcard URI. URI can be a URL (a web page address) or a URN representing another kind of resource such as DOI (Digital Object Identifier) or a PDF fingerprint.
`*` will match any character sequence (including an empty one), and a `_` will match any single character. Wildcards are only permitted within the path and query parts of the URI.
Escaping wildcards is not supported.
Examples of valid uris: http://foo.com/* urn:x-pdf:* file://localhost/_bc.pdf
Examples of invalid uris: *foo.com u_n:* file://* http://foo.com*
This feature is experimental and the API may change.
user=<str>
Limit the results to annotations made by the specified user.
group=<str>
Limit the results to annotations made in the specified group.
tag=<str>
Limit the results to annotations tagged with the specified value.
tags=<str>
Alias of `tag`.
any=<str>
Limit the results to annotations whose quote, tags, text or url fields
contain this keyword.
group=<str>
Limit the results to this group of annotations.
quote=<str>
Limit the results to annotations that contain this text
inside the text that was annotated.
references=<str>
Returns annotations that are replies to this parent annotation id.
text=<str>
Limit the results to annotations that contain this text in their textual body.
"""
response = self._ss.get(self.url_for("search"),json=kwargs)
debug_print(self.__class__.__name__,response)
return response.json()
def list(self, **kwargs):
response = self.search(user=self.user, **kwargs)
return response
def create(self, **kwargs): #Responses: 200,400
"""
Create an annotation.
Keyword arguments:
group=<str>
permissions=<object (Permissions)>
{
"read": <list userid>
"admin": <list userid>
"update": <list userid>
"delete": <list userid>
}
references=<list of str>
tags=<list of str>
target=<list>
[
selector=<list>
[
type=<str>(required)
]
]
text=<str>
uri=<str>
"""
try:
response = self._ss.post(self.url_for("annotations"),json=kwargs)
debug_print(self.__class__.__name__,response)
response.raise_for_status()
except requests.exceptions.HTTPError as err:
print(err)
return response.json()
def update(self, id, **kwargs): #200, 400, 404
"""
Update an existing annotation.
Keyword arguments:
annot_id -- <str> ID of annotation to return.
group: <str>
permissions: <object (Permissions)>
references: <list of str>
tags: <list of str>
target: <list>
[
selector: <list>
[
type: <str>(required)
]
]
text: <str>
uri: <str>
"""
try:
response = self._ss.patch(self.url_for("annotations",id),json=kwargs)
debug_print(self.__class__.__name__,response)
response.raise_for_status()
except requests.exceptions.HTTPError as err:
print(err)
return response.json()
def delete(self, annot_id): #200, 404
"""
Delete an annotation.
Keyword arguments:
annot_id -- <str>(required) ID of annotation to delete.
"""
try:
response = self._ss.delete(self.url_for("annotations",annot_id))
debug_print(self.__class__.__name__,response)
response.raise_for_status()
except requests.exceptions.HTTPError as err:
print(err)
return response.json()