Time Workday RaaS Requests
I've had a few scenarios recently where I've needed to time RaaS API requests from Workday for testing and optimizing reports.
This is a somewhat unpolished python script with inline dependencies I can quickly run with uv.
Using the script
With uv installed, it's as easy as uv run time_requests.py.
Don't forget to change the DEFAULT_URL in the script or specify --url.
uv run time_requests.py -h
usage: time_requests.py [-h] [--url URL] [--user USER] [--count COUNT] [--timeout TIMEOUT] [--download]
Time a Workday RaaS request.
options:
-h, --help show this help message and exit
--url URL The target URL to request.
--user USER Workday ISU Username
--count COUNT Number of times to run the request (default=1)
--timeout TIMEOUT Request timeout in minutes (default=1)
--download Save the output as a timestamped CSV file
The script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests",
# ]
# ///
#!/usr/bin/env python
from __future__ import annotations
import argparse
import datetime
import getpass
import os
import time
from collections.abc import Sequence
from pathlib import Path
import requests
from requests.auth import HTTPBasicAuth
# Default configuration
DEFAULT_URL = "TODO"
def log(message: str) -> None:
"""Helper to print messages with a timestamp."""
print(f"[{datetime.datetime.now().isoformat()}] {message}")
def format_size(bytes_size: int) -> str:
"""Formats bytes into a human-readable string with commas."""
mb_size = bytes_size / (1024 * 1024)
return f"{bytes_size:,} bytes ({mb_size:,.2f} MB)"
def time_http_request(
url: str,
username: str,
password: str,
timeout_minutes: int = 1,
download: bool = False,
) -> datetime.timedelta | None:
"""Performs the HTTP request, measures duration, and optionally saves the file."""
start_time = datetime.datetime.now()
log("Timer started. Sending request...")
try:
response = requests.get(
url,
auth=HTTPBasicAuth(username, password),
timeout=timeout_minutes * 60,
stream=True,
)
response.raise_for_status()
line_count = 0
total_bytes = 0
if download:
# Generate filename based on current timestamp
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{timestamp}.csv"
filepath = Path(__file__).parent / filename
# Download the file
log(f"Downloading content to: {filename}")
with open(filepath, "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
log("Download complete.")
# Get file size and line count from the saved file
total_bytes = os.path.getsize(filepath)
with open(filepath, "rb") as f:
while chunk := f.read(1024 * 1024):
line_count += chunk.count(b"\n")
else:
# Consume content to ensure request is fully complete without saving
# We use iter_lines to count lines, but track raw length for size
for line in response.iter_lines(decode_unicode=True):
if line:
line_count += 1
# Approximate size for in-memory streaming
total_bytes += len(line.encode("utf-8")) + 1
log(f"Line Count: {line_count:,}")
log(f"File Size: {format_size(total_bytes)}")
end_time = datetime.datetime.now()
duration = end_time - start_time
log(f"Request completed. Status Code: {response.status_code}")
log(f"Total Duration: {duration}")
return duration
except Exception as e:
fail_time = datetime.datetime.now()
duration = fail_time - start_time
log(f"Request FAILED. Error: {e}")
log(f"Total Duration until failure: {duration}")
sleep_duration = (timeout_minutes / 2) * 60
log(f"Sleeping for {timeout_minutes / 2} minutes before continuing...")
time.sleep(sleep_duration)
return duration
def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Time a Workday RaaS request.")
parser.add_argument("--url", default=DEFAULT_URL, help="The target URL to request.")
parser.add_argument("--user", help="Workday ISU Username")
parser.add_argument(
"--count",
type=int,
default=1,
help="Number of times to run the request (default=1)",
)
parser.add_argument(
"--timeout", type=int, default=1, help="Request timeout in minutes (default=1)"
)
parser.add_argument(
"--download",
action="store_true",
help="Save the output as a timestamped CSV file",
)
args = parser.parse_args(argv)
# Handle Credentials
username = args.user or input("Enter Username: ")
password = getpass.getpass("Enter Password: ")
url = args.url or input("Enter URL: ")
results: list[datetime.timedelta | None] = []
# Request/download the report n times
for i in range(1, args.count + 1):
print(f"\nPreparing request {i:02d}/{args.count:02d}")
print("-" * 30)
duration = time_http_request(
url=url,
username=username,
password=password,
timeout_minutes=args.timeout,
download=args.download if i == 1 else False, # Download the first time only
)
results.append(duration)
print("\n")
# Final Summary in Minutes
print("=" * 30)
print("SUMMARY (Minutes)")
print("=" * 30)
formatted_results = []
for i, delta in enumerate(results, 1):
if delta:
# Convert total seconds to minutes
mins = delta.total_seconds() / 60
formatted_results.append(f"{mins:.2f}")
print(f"Run {i:02d}: {mins:.2f} min")
else:
formatted_results.append("FAILED")
print(f"Run {i:02d}: FAILED")
print(f"\nAll Durations (min): {formatted_results}")
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except KeyboardInterrupt:
print("\nProcess interrupted by user.")
SystemExit(1)