improves documentation, tabbing and error handling in APIHandler class
Claude Code helped with autocompletion, the rest is my work
This commit is contained in:
@@ -4,7 +4,18 @@ import elabapi_python as elabapi
|
|||||||
|
|
||||||
class APIHandler:
|
class APIHandler:
|
||||||
"""
|
"""
|
||||||
Class to standardize the format of the headers of our http requests.
|
Class which handles all interactions with the eLabFTW API.
|
||||||
|
It provides methods to retrieve data from the API and download attachments.
|
||||||
|
It relies minimally on the elabapi-python library, which is used only for downloading attachments
|
||||||
|
(since the API doesn't support downloading attachments AFAIK).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
api_key: A valid API key for the eLabFTW instance where the data is stored, with permissions to access the relevant entries.
|
||||||
|
eLabFTW's API keys are well documented here: https://doc.elabftw.net/docs/usage/api/.
|
||||||
|
If you don't have an API key and are uncapable of creating one, contact your eLabFTW administrator.
|
||||||
|
Or RTFM and create one yourself, it's not that hard.
|
||||||
|
ELABFTW_API_URL: Complete URL of the eLabFTW instance's root for the API endpoints.
|
||||||
|
In full caps because it won't (shouldn't) be changed much.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TO-DO: remove static url.
|
# TO-DO: remove static url.
|
||||||
@@ -20,40 +31,54 @@ class APIHandler:
|
|||||||
|
|
||||||
def get_entry_from_elabid(self, elabid, entryType="items"):
|
def get_entry_from_elabid(self, elabid, entryType="items"):
|
||||||
"""
|
"""
|
||||||
Method which returns a resource's raw data (as dictionary) from its elabid and entry type.
|
Returns raw data (as dictionary) from its elabid and entry type.
|
||||||
|
|
||||||
Entry type can be either "experiments" or "items".
|
args:
|
||||||
|
elabid: elabftw internal id of the selected resource.
|
||||||
|
entryType: Resource type. Anything other than "experiments" or "items" WILL raise an error.
|
||||||
"""
|
"""
|
||||||
# TO-DO: validation and error handling on entryType value.
|
if entryType not in ["experiments", "items"]:
|
||||||
|
raise Exception(
|
||||||
|
"You can only download attachments from experiments or items."
|
||||||
|
)
|
||||||
|
|
||||||
header = self.header
|
header = self.header
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
headers=header, url=f"{self.elaburl}/{entryType}/{elabid}", verify=True
|
headers=header, url=f"{self.elaburl}/{entryType}/{elabid}", verify=True
|
||||||
)
|
)
|
||||||
if response.status_code // 100 in [1, 2, 3]:
|
|
||||||
entry_data = response.json()
|
# Response is 5xx = server error:
|
||||||
return entry_data
|
if response.status_code // 100 == 5:
|
||||||
elif response.status_code // 100 == 4:
|
raise ConnectionError(
|
||||||
|
f"There's a problem on the server. Status code: {response.status_code}."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Response is 4xx = client error:
|
||||||
|
if response.status_code // 100 == 4:
|
||||||
match response.status_code:
|
match response.status_code:
|
||||||
case 401 | 403:
|
case 401 | 403:
|
||||||
|
# Forbidden or unauthorized:
|
||||||
raise ConnectionError(
|
raise ConnectionError(
|
||||||
f"Invalid API key, authentication method or elabid. Check if an item with ID = {elabid} actually exists."
|
f"Invalid API key, authentication method or elabid. Check if an item with ID = {elabid} actually exists."
|
||||||
)
|
)
|
||||||
case 404:
|
case 404:
|
||||||
|
# Lapalissian:
|
||||||
raise ConnectionError(
|
raise ConnectionError(
|
||||||
f"404: Not Found. This means there's no resource with this elabid (wrong elabid?) on your eLabFTW (wrong endpoint?)."
|
f"404: Not Found. This means there's no resource with this elabid (wrong elabid?) on your eLabFTW (wrong endpoint?)."
|
||||||
)
|
)
|
||||||
case 400:
|
case 400:
|
||||||
|
# I genuinely have no idea:
|
||||||
raise ConnectionError(
|
raise ConnectionError(
|
||||||
f"400: Bad Request. This means the API endpoint you tried to reach is invalid. Did you tamper with the source code? If not, contact the developer."
|
f"400: Bad Request. This means the API endpoint you tried to reach is invalid. Did you tamper with the source code? If not, contact the developer."
|
||||||
)
|
)
|
||||||
case _:
|
case _:
|
||||||
|
# For some fucking reason, this is the only error I actually get from the API...
|
||||||
raise ConnectionError(
|
raise ConnectionError(
|
||||||
f"HTTP request failed with status code: {response.status_code} (NOTE: 4xx means user's fault)."
|
f"HTTP request failed with status code: {response.status_code} (NOTE: 4xx means user's fault)."
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
raise ConnectionError(
|
entry_data = response.json()
|
||||||
f"There's a problem on the server. Status code: {response.status_code}."
|
return entry_data
|
||||||
)
|
|
||||||
|
|
||||||
def download_attachments_data(self, elabid, entryType="experiments"):
|
def download_attachments_data(self, elabid, entryType="experiments"):
|
||||||
"""
|
"""
|
||||||
@@ -123,4 +148,3 @@ class APIHandler:
|
|||||||
with open(os.path.join(dump_dir, f"exp{elabid}-{file}"), "wb") as f:
|
with open(os.path.join(dump_dir, f"exp{elabid}-{file}"), "wb") as f:
|
||||||
f.write(raw_data)
|
f.write(raw_data)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user