Skip to content

Commit 8157ecc

Browse files
committed
feat: allow to read alphafold server format
1 parent 9d47732 commit 8157ecc

1 file changed

Lines changed: 64 additions & 49 deletions

File tree

examples/science/af3-slurm/examples/simple_ipynb_launcher/adm/slurm_client.py.j2

Lines changed: 64 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,60 @@ def is_valid_path(input_path: str, expected_prefix: str) -> bool:
2727
return os.path.isabs(input_path) and input_path.startswith(expected_prefix)
2828

2929

30-
def sanitised_name(name: str) -> str:
31-
"""Returns sanitised version of the name that can be used as a filename."""
30+
def sanitize_name(name: str) -> str:
3231
"""Returns sanitised version of the name that can be used as a filename."""
3332
lower_spaceless_name = name.lower().replace(' ', '_')
3433
allowed_chars = set(string.ascii_lowercase + string.digits + '_-.')
3534
return ''.join(l for l in lower_spaceless_name if l in allowed_chars)
3635

3736

37+
def retrieve_sanitised_input_file_name(input_file: str) -> Optional[str]:
38+
"""Retrieves a sanitised version of the input file name."""
39+
if not os.path.exists(input_file):
40+
raise FileNotFoundError(f"Input file '{input_file}' does not exist.")
41+
try:
42+
with open(input_file, 'r') as f:
43+
parser = ijson.items(f, '')
44+
try:
45+
root = next(parser)
46+
except StopIteration:
47+
raise ValueError("Empty JSON file.")
48+
49+
# Case 1: root is a list
50+
if isinstance(root, list):
51+
if len(root) != 1:
52+
raise ValueError(
53+
"[ERROR] The AlphaFold Server format is not supported: the list must contain exactly one dictionary item.")
54+
item = root[0]
55+
if not isinstance(item, dict):
56+
raise ValueError(
57+
"[ERROR] Item in Alphafold Server item must be a dictionary.")
58+
59+
# Case 2: root is a dict
60+
elif isinstance(root, dict):
61+
item = root
62+
else:
63+
raise ValueError(
64+
"[ERROR] Top-level JSON must be a list or a dictionary.")
65+
66+
# Validate and extract 'name'
67+
name = item.get('name')
68+
if not isinstance(name, str):
69+
raise ValueError(
70+
"[ERROR] 'name' key must exist and be a string.")
71+
72+
return sanitize_name(name)
73+
74+
except FileNotFoundError:
75+
print(f"[ERROR] Input file '{input_file}' not found.")
76+
raise FileNotFoundError(
77+
f"[ERROR] Input file '{input_file}' not found.")
78+
except Exception as e:
79+
print(f"[ERROR] Error processing input file '{input_file}': {e}")
80+
raise Exception(
81+
f"[ERROR] Error processing input file '{input_file}': {e}")
82+
83+
3884
class AF3SlurmClient:
3985
"""An AF3 client for interacting with the Slurm API."""
4086
SLURM_API_VERSION = "v0.0.41"
@@ -68,7 +114,7 @@ class AF3SlurmClient:
68114
self.slurm_base_url = f"http://{hostname}:{port}/slurm/{self.SLURM_API_VERSION}"
69115
self.slurmdb_base_url = f"http://{hostname}:{port}/slurmdb/{self.SLURM_API_VERSION}"
70116
self.token = token
71-
117+
72118
# Af3 default config
73119
self.af3_config = {
74120
"datapipeline_timeout": "{{ default_datapipeline_timeout }}",
@@ -183,33 +229,17 @@ class AF3SlurmClient:
183229
inference_model_cif_output_path: Optional[str] = None
184230
inference_summary_confidences_output_path: Optional[str] = None
185231
inference_confidences_output_path: Optional[str] = None
186-
try:
187-
sanitised_input_name: Optional[str] = None
188-
with open(input_file, "r") as f:
189-
name = None
190-
parser = ijson.parse(f)
191-
for prefix, event, value in parser:
192-
if prefix == "name" and event == "string":
193-
name = value
194-
break
195-
if not name:
196-
raise ValueError("Missing 'name' in input file.")
197-
sanitised_input_name = sanitised_name(name)
198-
199-
inference_model_cif_output_path = os.path.join(
200-
output_dir, f"{sanitised_input_name}", f"{sanitised_input_name}_model.cif")
201-
inference_summary_confidences_output_path = os.path.join(
202-
output_dir, f"{sanitised_input_name}", f"{sanitised_input_name}_summary_confidences.json")
203-
inference_confidences_output_path = os.path.join(
204-
output_dir, f"{sanitised_input_name}", f"{sanitised_input_name}_confidences.json")
205-
except FileNotFoundError:
206-
print(f"[ERROR] Input file '{input_file}' not found.")
207-
raise FileNotFoundError(
208-
f"[ERROR] Input file '{input_file}' not found.")
209-
except Exception as e:
210-
print(f"[ERROR] Error processing input file '{input_file}': {e}")
211-
raise Exception(
212-
f"[ERROR] Error processing input file '{input_file}': {e}")
232+
233+
sanitised_input_name: Optional[str] = retrieve_sanitised_input_file_name(
234+
input_file=input_file
235+
)
236+
237+
inference_model_cif_output_path = os.path.join(
238+
output_dir, f"{sanitised_input_name}", f"{sanitised_input_name}_model.cif")
239+
inference_summary_confidences_output_path = os.path.join(
240+
output_dir, f"{sanitised_input_name}", f"{sanitised_input_name}_summary_confidences.json")
241+
inference_confidences_output_path = os.path.join(
242+
output_dir, f"{sanitised_input_name}", f"{sanitised_input_name}_confidences.json")
213243

214244
job_config = {
215245
"account": self.af3_config["service_user"],
@@ -261,25 +291,10 @@ class AF3SlurmClient:
261291
f"{job_type}_result", file_name, f"{timestamp}"
262292
)
263293

264-
datapipeline_output_file_path: Optional[str] = None
265-
try:
266-
sanitised_input_name: Optional[str] = None
267-
with open(input_file, "r") as f:
268-
name = None
269-
parser = ijson.parse(f)
270-
for prefix, event, value in parser:
271-
if prefix == "name" and event == "string":
272-
name = value
273-
break
274-
if not name:
275-
raise ValueError("Missing 'name' in input file.")
276-
sanitised_input_name = sanitised_name(name)
277-
datapipeline_output_file_path = os.path.join(
278-
output_dir, f"{sanitised_input_name}", f"{sanitised_input_name}_data.json")
279-
except Exception as e:
280-
print(f"[ERROR] Error processing input file '{input_file}': {e}")
281-
raise Exception(
282-
f"[ERROR] Error processing input file '{input_file}': {e}")
294+
sanitised_input_name = retrieve_sanitised_input_file_name(input_file)
295+
datapipeline_output_file_path = os.path.join(
296+
output_dir, f"{sanitised_input_name}", f"{sanitised_input_name}_data.json")
297+
283298
job_config = {
284299
"account": self.af3_config["service_user"],
285300
"tasks": 1,

0 commit comments

Comments
 (0)