Skip to content

Commit

Permalink
Updated click function to send functionality to other functions
Browse files Browse the repository at this point in the history
moved user error checking to another function
updated that formatting function to use SystemExit and and added "Error:" to the front of two of the messages
updated the pytests to account for the error message and code success changes

moved create message to another function

added a function to turn the monitor models into dictionaries to make it easier in the FastAPI web page

updated the complex and simple prints for the dictionary
  • Loading branch information
Will-Cross1 committed Oct 22, 2024
1 parent 3c84ea3 commit 398005d
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 97 deletions.
327 changes: 238 additions & 89 deletions nlds_utils/nlds_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,59 +90,246 @@ def query_monitor_db(
return trec


def print_simple_monitor(record_list, stat_string):
def print_simple_monitor(records_dict_list, stat_string):
"""Print a multi-line set of status for monitor"""
click.echo(stat_string)
click.echo(
f"{'':<4}{'user':<16}{'group':<16}{'id':<6}{'action':<16}{'job label':<16}"
f"{'state':<23}{'last update':<20}"
)
for record in record_list:
state = record.get_state()
if "job_label" in record.__dict__ and record.__dict__["job_label"]:
job_label = record.job_label
else:
job_label = ""
for record in records_dict_list:
click.echo(
f"{'':<4}{record.user:<16}{record.group:<16}{record.id:<6}"
f"{record.api_action:<16}{job_label:16}{state.name:<23}"
f"{(record.creation_time)}"
f"{'':<4}{record['user']:<16}{record['group']:<16}{record['id']:<6}"
f"{record['api_action']:<16}{record['job_label']:16}{record['state']:<23}"
f"{(record['creation_time'])}"
)


def print_complex_monitor(record_list, stat_string):
def print_complex_monitor(records_dict_list, stat_string):
"""Print a multi-line set of status for monitor in more detail, with a list of
failed files if necessary"""
click.echo(stat_string)
for record in record_list:
for record in records_dict_list:
click.echo("")
click.echo(f"{'':<4}{'id':<16}: {record.id}")
click.echo(f"{'':<4}{'user':<16}: {record.user}")
click.echo(f"{'':<4}{'group':<16}: {record.group}")
click.echo(f"{'':<4}{'action':<16}: {record.api_action}")
click.echo(f"{'':<4}{'transaction id':<16}: {record.transaction_id}")
click.echo(f"{'':<4}{'creation time':<16}: {(record.creation_time)}")
state = record.get_state()
click.echo(f"{'':<4}{'state':<16}: {state.name}")
if "warnings" in record.__dict__:
warn_str = ""
for w in record.warnings:
warn_str += w.warning + f"\n{'':<22}"
click.echo(f"{'':<4}{'warnings':<16}: {warn_str[:-23]}")
click.echo(f"{'':<4}{'id':<16}: {record['id']}")
click.echo(f"{'':<4}{'user':<16}: {record['user']}")
click.echo(f"{'':<4}{'group':<16}: {record['group']}")
click.echo(f"{'':<4}{'action':<16}: {record['api_action']}")
click.echo(f"{'':<4}{'transaction id':<16}: {record['transaction_id']}")
click.echo(f"{'':<4}{'creation time':<16}: {(record['creation_time'])}")
click.echo(f"{'':<4}{'state':<16}: {record['state']}")

warn_str = ""
for w in record["warnings"]:
warn_str += w["warning"] + f"\n{'':<22}"
click.echo(f"{'':<4}{'warnings':<16}: {warn_str[:-23]}")

click.echo(f"{'':<4}{'sub records':<16}->")
for sr in record.sub_records:
sr_dict = sr.__dict__
click.echo(f"{'':4}{'+':<4} {'id':<13}: {sr.id}")
click.echo(f"{'':<9}{'sub_id':<13}: {sr.sub_id}")
click.echo(f"{'':<9}{'state':<13}: {sr.state.name}")
click.echo(f"{'':<9}{'last update':<13}: {(sr.last_updated)}")

if len(sr_dict["failed_files"]) > 0:
for sr in record["sub_records"]:
click.echo(f"{'':4}{'+':<4} {'id':<13}: {sr['id']}")
click.echo(f"{'':<9}{'sub_id':<13}: {sr['sub_id']}")
click.echo(f"{'':<9}{'state':<13}: {sr['state']}")
click.echo(f"{'':<9}{'last update':<13}: {(sr['last_updated'])}")

if len(sr["failed_files"]) > 0:
click.echo(f"{'':<9}{'failed files':<13}->")
for ff in sr.failed_files:
click.echo(f"{'':<9}{'+':<4} {'filepath':<8} : {ff.filepath}")
click.echo(f"{'':<9}{'':>4} {'reason':<8} : {ff.reason}")
for ff in sr["failed_files"]:
click.echo(f"{'':<9}{'+':<4} {'filepath':<8} : {ff['filepath']}")
click.echo(f"{'':<9}{'':>4} {'reason':<8} : {ff['reason']}")


def validate_inputs(start_time, end_time, state, record_state):
"""Ensures user inputs are valid before querying the database"""
try:
if start_time and end_time:
if start_time > end_time:
raise ValueError("Error: Start time must be before end time.")

if state:
try:
state = State[state.upper()]
except KeyError:
raise ValueError(f"Error: Invalid state: {state}")

if record_state:
try:
record_state = State[record_state.upper()]
except KeyError:
raise ValueError(f"Error: Invalid state: {record_state}")
except ValueError as e:
raise SystemExit(e)
return state, record_state


def construct_stat_string(
id,
transaction_id,
user,
group,
state,
record_state,
start_time,
end_time,
order,
):
"""
Constructs a status string based on the inputs and prints the response.
"""
details = []

# Construct details based on provided values
stat_string = "State of transactions for "
if id:
details.append(f"id: {id}")
elif transaction_id:
details.append(f"transaction id: {transaction_id}")
else:
if user:
details.append(f"user: {user}")
if group:
details.append(f"group: {group}")
if state:
details.append(f"state: {state.name}")
if record_state:
details.append(f"record state: {record_state.name}")
if start_time and end_time:
details.append(f"between: {start_time} and {end_time}")
elif start_time and not end_time:
details.append(f"from: {start_time}")

# If no details were added, set req_details to "all records"
if not details:
req_details = "all records"
else:
# Join the provided details with commas
req_details = ", ".join(details)

# Add order to the request details
if order:
req_details += f", order: {order}"

stat_string += req_details

return stat_string


def construct_record_dict(
query,
):
"""
Turn the monitor models into a large list of dictionarys to make it easier
for the fastAPI web page
"""
records_dict = []

for record in query:
# Create an empty dictionary for the TransactionRecord
record_dict = {}

# Specify the order of fields
field_order = [
"id",
"transaction_id",
"user",
"group",
"job_label",
"api_action",
"creation_time",
"state",
"warnings",
"sub_records",
]

for key in field_order:
if key == "state":
state = record.get_state()
record_dict[key] = state.name
else:
if hasattr(record, key):
value = getattr(record, key)
record_dict[key] = value

# Convert sub_records to dictionaries
if record.sub_records:
record_dict["sub_records"] = []
for sub_record in record.sub_records:
# Create an empty dictionary for the SubRecord
sub_record_dict = {}
sub_field_order = [
"id",
"sub_id",
"state",
"retry_count",
"last_updated",
"failed_files",
"transaction_record_id",
] # Specify sub_record field order

for key in sub_field_order:
if hasattr(sub_record, key):
value = getattr(sub_record, key)
if key == "state":
sub_record_dict[key] = value.name
else:
sub_record_dict[key] = value

# Convert failed_files to dictionaries
if sub_record.failed_files:
sub_record_dict["failed_files"] = []
for failed_file in sub_record.failed_files:
failed_file_dict = {}
failed_field_order = [
"id",
"filepath",
"reason",
"sub_record_id",
] # Specify failed_file field order

for key in failed_field_order:
if hasattr(failed_file, key):
value = getattr(failed_file, key)
failed_file_dict[key] = value

sub_record_dict["failed_files"].append(failed_file_dict)
else:
sub_record_dict["failed_files"] = (
[]
) # If there are no failed_files, assign an empty list

# Append the constructed sub_record_dict to the sub_records list
record_dict["sub_records"].append(sub_record_dict)

else:
record_dict["sub_records"] = (
[]
) # If there are no sub_records, assign an empty list

# Convert warnings to dictionaries
if record.warnings:
record_dict["warnings"] = []
for warning in record.warnings:
warning_dict = {}
warning_field_order = [
"id",
"warning",
"transaction_record_id",
] # Specify warning field order

for key in warning_field_order:
if hasattr(warning, key):
value = getattr(warning, key)
warning_dict[key] = value

record_dict["warnings"].append(warning_dict)
else:
record_dict["warnings"] = (
[]
) # If there are no warnings, assign an empty list

# Append the constructed record_dict to records_dict
records_dict.append(record_dict)

return records_dict


@click.command()
Expand Down Expand Up @@ -237,24 +424,7 @@ def view_jobs(
else:
order = "descending"

if start_time and end_time:
if start_time > end_time:
click.echo("Error: Start time must be before end time.")
exit()

if state:
try:
state = State[state.upper()]
except KeyError:
click.echo(f"Invalid state: {state}")
exit()

if record_state:
try:
record_state = State[record_state.upper()]
except KeyError:
click.echo(f"Invalid state: {record_state}")
exit()
state, record_state = validate_inputs(start_time, end_time, state, record_state)

# Connect to the monitor database
session = connect_to_monitor()
Expand All @@ -271,45 +441,24 @@ def view_jobs(
order,
)

details = []

# Check if each value is provided and add to details
stat_string = "State of transactions for "
if id:
details.append(f"id: {id}")
elif transaction_id:
details.append(f"transaction id: {transaction_id}")
else:
if user:
details.append(f"user: {user}")
if group:
details.append(f"group: {group}")
if state:
details.append(f"state: {state.name}")
if record_state:
details.append(f"record state: {record_state.name}")
if start_time and end_time:
details.append(f"between: {start_time} and {end_time}")
if start_time and not end_time:
details.append(f"from: {start_time}")

# If no details were added, set req_details to 'all records'
if not details:
req_details = "all records"
else:
# Join the provided details with commas
req_details = ", ".join(details)

# Add the order
if order:
req_details += f", order: {order}"
stat_string = construct_stat_string(
id,
transaction_id,
user,
group,
state,
record_state,
start_time,
end_time,
order,
)

stat_string += req_details
records_dict_list = construct_record_dict(query)

if complex or id or transaction_id:
print_complex_monitor(query, stat_string)
print_complex_monitor(records_dict_list, stat_string)
else:
print_simple_monitor(query, stat_string)
print_simple_monitor(records_dict_list, stat_string)


if __name__ == "__main__":
Expand Down
Loading

0 comments on commit 398005d

Please sign in to comment.