dayhoff-tools 1.3.3__py3-none-any.whl → 1.3.5__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.
- dayhoff_tools/cli/engine_commands.py +68 -11
- {dayhoff_tools-1.3.3.dist-info → dayhoff_tools-1.3.5.dist-info}/METADATA +1 -1
- {dayhoff_tools-1.3.3.dist-info → dayhoff_tools-1.3.5.dist-info}/RECORD +5 -5
- {dayhoff_tools-1.3.3.dist-info → dayhoff_tools-1.3.5.dist-info}/WHEEL +0 -0
- {dayhoff_tools-1.3.3.dist-info → dayhoff_tools-1.3.5.dist-info}/entry_points.txt +0 -0
@@ -3,7 +3,7 @@
|
|
3
3
|
import json
|
4
4
|
import subprocess
|
5
5
|
import sys
|
6
|
-
from datetime import datetime, timedelta
|
6
|
+
from datetime import datetime, timedelta, timezone
|
7
7
|
from pathlib import Path
|
8
8
|
from typing import Dict, List, Optional, Tuple
|
9
9
|
|
@@ -130,15 +130,31 @@ def parse_launch_time(launch_time_str: str) -> datetime:
|
|
130
130
|
formats = [
|
131
131
|
"%Y-%m-%dT%H:%M:%S.%fZ",
|
132
132
|
"%Y-%m-%dT%H:%M:%SZ",
|
133
|
+
"%Y-%m-%dT%H:%M:%S%z", # ISO format with timezone
|
134
|
+
"%Y-%m-%dT%H:%M:%S+00:00", # Explicit UTC offset
|
133
135
|
"%Y-%m-%d %H:%M:%S",
|
134
136
|
]
|
137
|
+
|
138
|
+
# First try parsing with fromisoformat for better timezone handling
|
139
|
+
try:
|
140
|
+
# Handle the ISO format properly
|
141
|
+
return datetime.fromisoformat(launch_time_str.replace('Z', '+00:00'))
|
142
|
+
except (ValueError, AttributeError):
|
143
|
+
pass
|
144
|
+
|
145
|
+
# Fallback to manual format parsing
|
135
146
|
for fmt in formats:
|
136
147
|
try:
|
137
|
-
|
148
|
+
parsed = datetime.strptime(launch_time_str, fmt)
|
149
|
+
# If no timezone info, assume UTC
|
150
|
+
if parsed.tzinfo is None:
|
151
|
+
parsed = parsed.replace(tzinfo=timezone.utc)
|
152
|
+
return parsed
|
138
153
|
except ValueError:
|
139
154
|
continue
|
155
|
+
|
140
156
|
# Fallback: assume it's recent
|
141
|
-
return datetime.
|
157
|
+
return datetime.now(timezone.utc)
|
142
158
|
|
143
159
|
|
144
160
|
def format_status(state: str, ready: Optional[bool]) -> str:
|
@@ -363,7 +379,7 @@ def list_engines(
|
|
363
379
|
total_cost = 0.0
|
364
380
|
for engine in engines:
|
365
381
|
launch_time = parse_launch_time(engine["launch_time"])
|
366
|
-
uptime = datetime.
|
382
|
+
uptime = datetime.now(timezone.utc) - launch_time
|
367
383
|
hourly_cost = HOURLY_COSTS.get(engine["engine_type"], 0)
|
368
384
|
|
369
385
|
if engine["state"].lower() == "running":
|
@@ -418,7 +434,7 @@ def engine_status(
|
|
418
434
|
|
419
435
|
# Calculate costs
|
420
436
|
launch_time = parse_launch_time(engine["launch_time"])
|
421
|
-
uptime = datetime.
|
437
|
+
uptime = datetime.now(timezone.utc) - launch_time
|
422
438
|
hourly_cost = HOURLY_COSTS.get(engine["engine_type"], 0)
|
423
439
|
total_cost = hourly_cost * (uptime.total_seconds() / 3600)
|
424
440
|
|
@@ -550,7 +566,7 @@ def terminate_engine(
|
|
550
566
|
|
551
567
|
# Calculate cost
|
552
568
|
launch_time = parse_launch_time(engine["launch_time"])
|
553
|
-
uptime = datetime.
|
569
|
+
uptime = datetime.now(timezone.utc) - launch_time
|
554
570
|
hourly_cost = HOURLY_COSTS.get(engine["engine_type"], 0)
|
555
571
|
total_cost = hourly_cost * (uptime.total_seconds() / 3600)
|
556
572
|
|
@@ -1001,10 +1017,19 @@ def studio_status():
|
|
1001
1017
|
return
|
1002
1018
|
|
1003
1019
|
# Create status panel
|
1020
|
+
# Format status with colors
|
1021
|
+
status = studio['status']
|
1022
|
+
if status == "in-use":
|
1023
|
+
status_display = "[magenta]attached[/magenta]"
|
1024
|
+
elif status in ["attaching", "detaching"]:
|
1025
|
+
status_display = f"[yellow]{status}[/yellow]"
|
1026
|
+
else:
|
1027
|
+
status_display = f"[green]{status}[/green]"
|
1028
|
+
|
1004
1029
|
status_lines = [
|
1005
1030
|
f"[bold]Studio ID:[/bold] {studio['studio_id']}",
|
1006
1031
|
f"[bold]User:[/bold] {studio['user']}",
|
1007
|
-
f"[bold]Status:[/bold] {
|
1032
|
+
f"[bold]Status:[/bold] {status_display}",
|
1008
1033
|
f"[bold]Size:[/bold] {studio['size_gb']}GB",
|
1009
1034
|
f"[bold]Created:[/bold] {studio['creation_date']}",
|
1010
1035
|
]
|
@@ -1214,6 +1239,13 @@ def list_studios(
|
|
1214
1239
|
console.print("No studios found.")
|
1215
1240
|
return
|
1216
1241
|
|
1242
|
+
# Get all engines to map instance IDs to names
|
1243
|
+
engines_response = make_api_request("GET", "/engines")
|
1244
|
+
engines = {}
|
1245
|
+
if engines_response.status_code == 200:
|
1246
|
+
for engine in engines_response.json().get("engines", []):
|
1247
|
+
engines[engine["instance_id"]] = engine["name"]
|
1248
|
+
|
1217
1249
|
# Create table
|
1218
1250
|
table = Table(title="Studios", box=box.ROUNDED)
|
1219
1251
|
table.add_column("Studio ID", style="cyan")
|
@@ -1224,14 +1256,39 @@ def list_studios(
|
|
1224
1256
|
table.add_column("Created")
|
1225
1257
|
|
1226
1258
|
for studio in studios:
|
1227
|
-
|
1259
|
+
# Change status display
|
1260
|
+
if studio["status"] == "in-use":
|
1261
|
+
status_display = "[magenta]attached[/magenta]"
|
1262
|
+
elif studio["status"] in ["attaching", "detaching"]:
|
1263
|
+
status_display = "[yellow]" + studio["status"] + "[/yellow]"
|
1264
|
+
else:
|
1265
|
+
status_display = "[green]available[/green]"
|
1266
|
+
|
1267
|
+
# Format attached engine info
|
1268
|
+
attached_to = "-"
|
1269
|
+
if studio.get("attached_vm_id"):
|
1270
|
+
vm_id = studio["attached_vm_id"]
|
1271
|
+
engine_name = engines.get(vm_id, "unknown")
|
1272
|
+
attached_to = f"{engine_name} ({vm_id})"
|
1273
|
+
|
1274
|
+
# Format creation date (remove microseconds and timezone info)
|
1275
|
+
created = studio["creation_date"]
|
1276
|
+
try:
|
1277
|
+
# Parse and reformat to just show date and time
|
1278
|
+
if 'T' in created:
|
1279
|
+
created_dt = datetime.fromisoformat(created.replace('Z', '+00:00'))
|
1280
|
+
created = created_dt.strftime("%Y-%m-%d %H:%M")
|
1281
|
+
except:
|
1282
|
+
# If parsing fails, just truncate
|
1283
|
+
created = created.split('T')[0] if 'T' in created else created[:16]
|
1284
|
+
|
1228
1285
|
table.add_row(
|
1229
1286
|
studio["studio_id"],
|
1230
1287
|
studio["user"],
|
1231
|
-
|
1288
|
+
status_display,
|
1232
1289
|
f"{studio['size_gb']}GB",
|
1233
|
-
|
1234
|
-
|
1290
|
+
attached_to,
|
1291
|
+
created,
|
1235
1292
|
)
|
1236
1293
|
|
1237
1294
|
console.print(table)
|
@@ -3,7 +3,7 @@ dayhoff_tools/chemistry/standardizer.py,sha256=uMn7VwHnx02nc404eO6fRuS4rsl4dvSPf
|
|
3
3
|
dayhoff_tools/chemistry/utils.py,sha256=jt-7JgF-GeeVC421acX-bobKbLU_X94KNOW24p_P-_M,2257
|
4
4
|
dayhoff_tools/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
dayhoff_tools/cli/cloud_commands.py,sha256=33qcWLmq-FwEXMdL3F0OHm-5Stlh2r65CldyEZgQ1no,40904
|
6
|
-
dayhoff_tools/cli/engine_commands.py,sha256=
|
6
|
+
dayhoff_tools/cli/engine_commands.py,sha256=BU5mi4Q_UTsg2CQLkn2XQaw0anUKwIp_GhMa_Zjlcns,48604
|
7
7
|
dayhoff_tools/cli/main.py,sha256=rgeEHD9lJ8SBCR34BTLb7gVInHUUdmEBNXAJnq5yEU4,4795
|
8
8
|
dayhoff_tools/cli/swarm_commands.py,sha256=5EyKj8yietvT5lfoz8Zx0iQvVaNgc3SJX1z2zQR6o6M,5614
|
9
9
|
dayhoff_tools/cli/utility_commands.py,sha256=qs8vH9TBFHsOPC3X8cU3qZigM3dDn-2Ytq4o_F2WubU,27874
|
@@ -27,7 +27,7 @@ dayhoff_tools/intake/uniprot.py,sha256=BZYJQF63OtPcBBnQ7_P9gulxzJtqyorgyuDiPeOJq
|
|
27
27
|
dayhoff_tools/logs.py,sha256=DKdeP0k0kliRcilwvX0mUB2eipO5BdWUeHwh-VnsICs,838
|
28
28
|
dayhoff_tools/sqlite.py,sha256=jV55ikF8VpTfeQqqlHSbY8OgfyfHj8zgHNpZjBLos_E,18672
|
29
29
|
dayhoff_tools/warehouse.py,sha256=8YbnQ--usrEgDQGfvpV4MrMji55A0rq2hZaOgFGh6ag,15896
|
30
|
-
dayhoff_tools-1.3.
|
31
|
-
dayhoff_tools-1.3.
|
32
|
-
dayhoff_tools-1.3.
|
33
|
-
dayhoff_tools-1.3.
|
30
|
+
dayhoff_tools-1.3.5.dist-info/METADATA,sha256=LmjC7aGDDP-Pj-LII3xRjc_G8CELn7KJEozkjMtGeTU,2842
|
31
|
+
dayhoff_tools-1.3.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
32
|
+
dayhoff_tools-1.3.5.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
|
33
|
+
dayhoff_tools-1.3.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|