dayhoff-tools 1.3.3__py3-none-any.whl → 1.3.4__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.
@@ -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
 
@@ -134,11 +134,15 @@ def parse_launch_time(launch_time_str: str) -> datetime:
134
134
  ]
135
135
  for fmt in formats:
136
136
  try:
137
- return datetime.strptime(launch_time_str, fmt)
137
+ parsed = datetime.strptime(launch_time_str, fmt)
138
+ # If the format includes 'Z', it's UTC
139
+ if fmt.endswith('Z'):
140
+ parsed = parsed.replace(tzinfo=timezone.utc)
141
+ return parsed
138
142
  except ValueError:
139
143
  continue
140
144
  # Fallback: assume it's recent
141
- return datetime.utcnow()
145
+ return datetime.now(timezone.utc)
142
146
 
143
147
 
144
148
  def format_status(state: str, ready: Optional[bool]) -> str:
@@ -363,7 +367,7 @@ def list_engines(
363
367
  total_cost = 0.0
364
368
  for engine in engines:
365
369
  launch_time = parse_launch_time(engine["launch_time"])
366
- uptime = datetime.utcnow() - launch_time
370
+ uptime = datetime.now(timezone.utc) - launch_time
367
371
  hourly_cost = HOURLY_COSTS.get(engine["engine_type"], 0)
368
372
 
369
373
  if engine["state"].lower() == "running":
@@ -418,7 +422,7 @@ def engine_status(
418
422
 
419
423
  # Calculate costs
420
424
  launch_time = parse_launch_time(engine["launch_time"])
421
- uptime = datetime.utcnow() - launch_time
425
+ uptime = datetime.now(timezone.utc) - launch_time
422
426
  hourly_cost = HOURLY_COSTS.get(engine["engine_type"], 0)
423
427
  total_cost = hourly_cost * (uptime.total_seconds() / 3600)
424
428
 
@@ -550,7 +554,7 @@ def terminate_engine(
550
554
 
551
555
  # Calculate cost
552
556
  launch_time = parse_launch_time(engine["launch_time"])
553
- uptime = datetime.utcnow() - launch_time
557
+ uptime = datetime.now(timezone.utc) - launch_time
554
558
  hourly_cost = HOURLY_COSTS.get(engine["engine_type"], 0)
555
559
  total_cost = hourly_cost * (uptime.total_seconds() / 3600)
556
560
 
@@ -1001,10 +1005,19 @@ def studio_status():
1001
1005
  return
1002
1006
 
1003
1007
  # Create status panel
1008
+ # Format status with colors
1009
+ status = studio['status']
1010
+ if status == "in-use":
1011
+ status_display = "[magenta]attached[/magenta]"
1012
+ elif status in ["attaching", "detaching"]:
1013
+ status_display = f"[yellow]{status}[/yellow]"
1014
+ else:
1015
+ status_display = f"[green]{status}[/green]"
1016
+
1004
1017
  status_lines = [
1005
1018
  f"[bold]Studio ID:[/bold] {studio['studio_id']}",
1006
1019
  f"[bold]User:[/bold] {studio['user']}",
1007
- f"[bold]Status:[/bold] {studio['status']}",
1020
+ f"[bold]Status:[/bold] {status_display}",
1008
1021
  f"[bold]Size:[/bold] {studio['size_gb']}GB",
1009
1022
  f"[bold]Created:[/bold] {studio['creation_date']}",
1010
1023
  ]
@@ -1214,6 +1227,13 @@ def list_studios(
1214
1227
  console.print("No studios found.")
1215
1228
  return
1216
1229
 
1230
+ # Get all engines to map instance IDs to names
1231
+ engines_response = make_api_request("GET", "/engines")
1232
+ engines = {}
1233
+ if engines_response.status_code == 200:
1234
+ for engine in engines_response.json().get("engines", []):
1235
+ engines[engine["instance_id"]] = engine["name"]
1236
+
1217
1237
  # Create table
1218
1238
  table = Table(title="Studios", box=box.ROUNDED)
1219
1239
  table.add_column("Studio ID", style="cyan")
@@ -1224,14 +1244,39 @@ def list_studios(
1224
1244
  table.add_column("Created")
1225
1245
 
1226
1246
  for studio in studios:
1227
- status_color = "green" if studio["status"] == "available" else "yellow"
1247
+ # Change status display
1248
+ if studio["status"] == "in-use":
1249
+ status_display = "[magenta]attached[/magenta]"
1250
+ elif studio["status"] in ["attaching", "detaching"]:
1251
+ status_display = "[yellow]" + studio["status"] + "[/yellow]"
1252
+ else:
1253
+ status_display = "[green]available[/green]"
1254
+
1255
+ # Format attached engine info
1256
+ attached_to = "-"
1257
+ if studio.get("attached_vm_id"):
1258
+ vm_id = studio["attached_vm_id"]
1259
+ engine_name = engines.get(vm_id, "unknown")
1260
+ attached_to = f"{engine_name} ({vm_id})"
1261
+
1262
+ # Format creation date (remove microseconds and timezone info)
1263
+ created = studio["creation_date"]
1264
+ try:
1265
+ # Parse and reformat to just show date and time
1266
+ if 'T' in created:
1267
+ created_dt = datetime.fromisoformat(created.replace('Z', '+00:00'))
1268
+ created = created_dt.strftime("%Y-%m-%d %H:%M")
1269
+ except:
1270
+ # If parsing fails, just truncate
1271
+ created = created.split('T')[0] if 'T' in created else created[:16]
1272
+
1228
1273
  table.add_row(
1229
1274
  studio["studio_id"],
1230
1275
  studio["user"],
1231
- f"[{status_color}]{studio['status']}[/{status_color}]",
1276
+ status_display,
1232
1277
  f"{studio['size_gb']}GB",
1233
- studio.get("attached_vm_id", "-"),
1234
- studio["creation_date"],
1278
+ attached_to,
1279
+ created,
1235
1280
  )
1236
1281
 
1237
1282
  console.print(table)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dayhoff-tools
3
- Version: 1.3.3
3
+ Version: 1.3.4
4
4
  Summary: Common tools for all the repos at Dayhoff Labs
5
5
  Author: Daniel Martin-Alarcon
6
6
  Author-email: dma@dayhofflabs.com
@@ -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=uqS46dGMaF5UGNbIJ3G1oY3QxD38jUNHiBAfYMBpmf4,46268
6
+ dayhoff_tools/cli/engine_commands.py,sha256=w0wzs4aQUdzeyAyhHH9lHxpJEG8acrbguQ4Rd0u5bA4,48179
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.3.dist-info/METADATA,sha256=mWVT1xFladhd-RuJOeESPAoFxe1FDKsJP-G9tTlO1yk,2842
31
- dayhoff_tools-1.3.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
32
- dayhoff_tools-1.3.3.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
33
- dayhoff_tools-1.3.3.dist-info/RECORD,,
30
+ dayhoff_tools-1.3.4.dist-info/METADATA,sha256=YZw82MtQpm4aWNzOfEl62Iov6ZRXtw6LfZ43eI9psKE,2842
31
+ dayhoff_tools-1.3.4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
32
+ dayhoff_tools-1.3.4.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
33
+ dayhoff_tools-1.3.4.dist-info/RECORD,,