Skip to content

Commit a6591c2

Browse files
committed
Move files to repo-specific handlers
1 parent c8d8336 commit a6591c2

5 files changed

Lines changed: 226 additions & 249 deletions

File tree

mars_lib/submission_handlers/__init__.py

Whitespace-only changes.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from mars_lib.models.isa_json import IsaJson
2+
import requests
3+
from mars_lib.isa_json import reduce_isa_json_for_target_repo
4+
from mars_lib.authentication import get_webin_auth_token
5+
from mars_lib.target_repo import TargetRepository
6+
7+
def submit_to_biosamples(
8+
isa_json: IsaJson,
9+
biosamples_credentials: dict[str, str],
10+
webin_token_url: str,
11+
biosamples_url: str,
12+
) -> requests.Response:
13+
params = {
14+
"webinjwt": get_webin_auth_token(
15+
biosamples_credentials, auth_base_url=webin_token_url
16+
)
17+
}
18+
headers = {"accept": "*/*", "Content-Type": "application/json"}
19+
result = requests.post(
20+
biosamples_url,
21+
headers=headers,
22+
params=params,
23+
json=reduce_isa_json_for_target_repo(
24+
isa_json, TargetRepository.BIOSAMPLES.value
25+
).model_dump(by_alias=True, exclude_none=True),
26+
)
27+
28+
if result.status_code == 200 and not result.json().get("errors", []):
29+
return result
30+
31+
body = (
32+
result.request.body.decode()
33+
if isinstance(result.request.body, bytes)
34+
else result.request.body or ""
35+
)
36+
raise requests.HTTPError(
37+
f"Request towards BioSamples failed!\nRequest:\nMethod:{result.request.method}\nStatus:{result.status_code}\nURL:{result.request.url}\nHeaders:{result.request.headers}\nBody:{body}"
38+
)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from click import Path
2+
3+
import requests
4+
5+
from mars_lib.ftp_upload import FTPUploader
6+
from mars_lib.isa_json import reduce_isa_json_for_target_repo
7+
from mars_lib.models.isa_json import IsaJson
8+
from mars_lib.target_repo import TargetRepository
9+
from typing import List
10+
11+
def submit_to_ena(
12+
isa_json: IsaJson, user_credentials: dict[str, str], submission_url: str
13+
) -> requests.Response:
14+
params = {
15+
"webinUserName": user_credentials["username"],
16+
"webinPassword": user_credentials["password"],
17+
}
18+
headers = {"accept": "*/*", "Content-Type": "application/json"}
19+
result = requests.post(
20+
submission_url,
21+
headers=headers,
22+
params=params,
23+
json=reduce_isa_json_for_target_repo(
24+
isa_json, TargetRepository.ENA.value
25+
).model_dump(by_alias=True, exclude_none=True),
26+
)
27+
28+
if result.status_code == 200 and not result.json().get("errors", []):
29+
return result
30+
31+
body = (
32+
result.request.body.decode()
33+
if isinstance(result.request.body, bytes)
34+
else result.request.body or ""
35+
)
36+
raise requests.HTTPError(
37+
f"Request towards ENA failed!\nRequest:\nMethod:{result.request.method}\nStatus:{result.status_code}\nURL:{submission_url}\nParams: ['webinUserName': {params.get('webinUserName')}, 'webinPassword': ****]\nHeaders:{result.request.headers}\nBody:{body}"
38+
)
39+
40+
41+
def upload_to_ena(
42+
file_paths: List[Path],
43+
user_credentials: dict[str, str],
44+
submission_url: str,
45+
file_transfer: str,
46+
):
47+
file_transfer = file_transfer.lower()
48+
49+
if file_transfer == "ftp":
50+
uploader = FTPUploader(
51+
submission_url,
52+
user_credentials["username"],
53+
user_credentials["password"],
54+
)
55+
uploader.upload(file_paths)
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import io
2+
import time
3+
import requests
4+
from typing import Any
5+
from mars_lib.authentication import get_metabolights_auth_token
6+
from mars_lib.isa_json import reduce_isa_json_for_target_repo
7+
from mars_lib.models.isa_json import IsaJson
8+
from mars_lib.target_repo import TargetRepository
9+
10+
11+
def upload_to_metabolights(
12+
file_paths: list[str],
13+
isa_json: IsaJson,
14+
metabolights_credentials: dict[str, str],
15+
metabolights_url: str,
16+
metabolights_token_url: str,
17+
file_transfer: str = "ftp",
18+
):
19+
data_upload_protocol = (
20+
"ftp" if not file_transfer or file_transfer.lower() == "ftp" else ""
21+
)
22+
23+
if not data_upload_protocol == "ftp":
24+
raise ValueError(
25+
f"Data upload protocol {data_upload_protocol} is not supported"
26+
)
27+
28+
token = get_metabolights_auth_token(
29+
metabolights_credentials, auth_url=metabolights_token_url
30+
)
31+
headers = {
32+
"accept": "application/json",
33+
"Authorization": f"Bearer {token}",
34+
}
35+
isa_json_str = reduce_isa_json_for_target_repo(
36+
isa_json, TargetRepository.METABOLIGHTS
37+
).investigation.model_dump_json(by_alias=True, exclude_none=True)
38+
json_file = io.StringIO(isa_json_str)
39+
40+
files = {"isa_json_file": ("isa_json.json", json_file)}
41+
result = None
42+
try:
43+
submission_response = requests.post(
44+
metabolights_url,
45+
headers=headers,
46+
files=files,
47+
timeout=120,
48+
)
49+
submission_response.raise_for_status()
50+
if submission_response.json().get("errors", []):
51+
response_body = submission_response.request.body
52+
if isinstance(response_body, bytes):
53+
response_body = response_body.decode("utf-8")
54+
raise requests.HTTPError(
55+
f"Request towards MetaboLights failed!\nRequest:\nMethod:{submission_response.request.method}\nStatus:{submission_response.status_code}\nURL:{submission_response.request.url}\nHeaders:{submission_response.request.headers}\nBody:{response_body}"
56+
)
57+
58+
result = submission_response.json()
59+
except Exception as exc:
60+
raise exc
61+
62+
validation_url = find_value_in_info_section("validation-url", result["info"])
63+
validation_status_url = find_value_in_info_section(
64+
"validation-status-url", result["info"]
65+
)
66+
ftp_credentials_url = find_value_in_info_section(
67+
"ftp-credentials-url", result["info"]
68+
)
69+
70+
if file_transfer == "ftp":
71+
ftp_credentials_response = requests.get(ftp_credentials_url, headers=headers)
72+
ftp_credentials_response.raise_for_status()
73+
ftp_credentials = ftp_credentials_response.json()
74+
ftp_base_path = ftp_credentials["ftpPath"] # noqa F841
75+
uploader = FTPUploader( # noqa F841
76+
ftp_credentials["ftpHost"],
77+
ftp_credentials["ftpUser"],
78+
ftp_credentials["ftpPassword"],
79+
)
80+
# TODO: Update after the uploader is implemented/tested
81+
# uploader.upload(file_paths, target_location=ftp_base_path)
82+
83+
validation_response = requests.post(validation_url, headers=headers)
84+
validation_response.raise_for_status()
85+
pool_time_in_seconds = 10
86+
max_pool_count = 100
87+
validation_status_response = None
88+
for _ in range(max_pool_count):
89+
timeout = False
90+
try:
91+
validation_status_response = requests.get(
92+
validation_status_url, headers=headers, timeout=30
93+
)
94+
validation_status_response.raise_for_status()
95+
except requests.exceptions.Timeout:
96+
timeout = True
97+
if not timeout:
98+
if validation_status_response is None:
99+
raise ValueError("Validation status response is None")
100+
validation_status = validation_status_response.json()
101+
validation_time = find_value_in_info_section(
102+
"validation-time", validation_status["info"], fail_gracefully=True
103+
)
104+
if validation_time:
105+
break
106+
time.sleep(pool_time_in_seconds)
107+
else:
108+
raise ValueError(f"Validation failed after {max_pool_count} iterations")
109+
110+
if validation_status_response:
111+
return validation_status_response
112+
113+
raise ValueError("Submission failed for MetaboLights")
114+
115+
116+
def find_value_in_info_section(
117+
key: str, info_section: list[Any], fail_gracefully: bool = False
118+
) -> Any:
119+
for info in info_section:
120+
if info["name"] == key:
121+
return info["message"]
122+
if fail_gracefully:
123+
return None
124+
raise ValueError(f"Name {key} not found in info section")

0 commit comments

Comments
 (0)