Skip to content

Commit f16c60b

Browse files
authored
fix(content-health-monitor): ensure we always try to get the current user's full name (#266)
* Ensure we try to get the current user's full name even when setup is required, and handle any exceptions gracefully without printing errors at the top of reports * Make the user name bold * Refactor import to import module * Fix test * Update manifest * Update manifest
1 parent ccc9500 commit f16c60b

File tree

5 files changed

+214
-48
lines changed

5 files changed

+214
-48
lines changed

extensions/content-health-monitor/content-health-monitor.qmd

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,72 +15,71 @@ import requests
1515
import datetime
1616
from posit import connect
1717
from IPython.display import HTML, display
18-
from content_health_utils import *
19-
from content_health_utils import MonitorState
18+
import content_health_utils as utils
2019
2120
# Create state object to share between this file and utils
22-
state = MonitorState()
21+
state = utils.MonitorState()
2322
2423
# Initialize state variables that are only used in this Quarto document
2524
show_error = False # Used to display on-screen error messages if API errors occur
2625
error_message = None # Error message to display if API errors occur
2726
error_guid = None # GUID that caused the error
2827
content_result = None # Variable to store content monitoring result
2928
29+
# Initialize current user name with the default value from utils
30+
current_user_name = utils.DEFAULT_USER_NAME # Will be updated if user info can be retrieved
31+
3032
# Read environment variables
31-
connect_server = get_env_var("CONNECT_SERVER", state) # Automatically provided by Connect, must be set when previewing locally
32-
api_key = get_env_var("CONNECT_API_KEY", state) # Automatically provided by Connect, must be set when previewing locally
33-
monitored_content_guid = get_env_var("MONITORED_CONTENT_GUID", state)
33+
connect_server = utils.get_env_var("CONNECT_SERVER", state) # Automatically provided by Connect, must be set when previewing locally
34+
api_key = utils.get_env_var("CONNECT_API_KEY", state) # Automatically provided by Connect, must be set when previewing locally
35+
monitored_content_guid = utils.get_env_var("MONITORED_CONTENT_GUID", state)
3436
3537
# Extract GUID if it's a string or URL containing a GUID
3638
if monitored_content_guid:
37-
monitored_content_guid, guid_error_message = extract_guid(monitored_content_guid)
39+
monitored_content_guid, guid_error_message = utils.extract_guid(monitored_content_guid)
3840
# Handle URL with no GUID error
3941
if guid_error_message:
4042
state.show_instructions = True
4143
state.instructions.append(guid_error_message)
4244
43-
# Only instantiate the client if we have the required environment variables
45+
# Check if we have the required environment variables to instantiate the client
4446
client = None
45-
if not state.show_instructions:
47+
has_connect_env_vars = connect_server and api_key
48+
49+
if has_connect_env_vars:
4650
try:
47-
# Instantiate a Connect client using posit-sdk where api_key and url are automatically read from our environment vars
51+
# Instantiate a Connect client using posit-sdk
4852
client = connect.Client()
53+
54+
# Get current user's full name - function handles errors internally
55+
user_name = utils.get_current_user_full_name(client)
56+
if user_name != "Unknown": # Only update if we got a valid name
57+
current_user_name = user_name
4958
except ValueError as e:
50-
state.show_instructions = True
51-
state.instructions.append(f"<b>Error initializing Connect client:</b> {str(e)}")
59+
if not state.show_instructions:
60+
state.show_instructions = True
61+
state.instructions.append(f"<b>Error initializing Connect client:</b> {str(e)}")
5262
5363
5464
# ------ DATA GATHERING SECTION ------ #
5565
# Always record the current time for reporting purposes
5666
check_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
5767
58-
# Initialize current user name with a default for display
59-
current_user_name = "the publisher" # Default, used if no name is available
60-
61-
# Get current user's full name if client is available
62-
if not state.show_instructions and client:
63-
try:
64-
user_name = get_current_user_full_name(client)
65-
if user_name: # Only update if we got a valid name
66-
current_user_name = user_name
67-
except Exception as e:
68-
print(f"Warning: Could not get current user name: {str(e)}")
6968
7069
# Only proceed with content validation if we have all requirements
7170
# (state.show_instructions=False already tells us env vars are set and client was initialized)
7271
if not state.show_instructions:
7372
try:
7473
# Check if server is reachable
75-
check_server_reachable(connect_server, api_key)
74+
utils.check_server_reachable(connect_server, api_key)
7675
7776
# Validate the content
78-
content_result = validate(client, monitored_content_guid, connect_server, api_key)
77+
content_result = utils.validate(client, monitored_content_guid, connect_server, api_key)
7978
8079
# Check for content-specific errors
81-
if has_error(content_result):
80+
if utils.has_error(content_result):
8281
show_error = True
83-
error_details = extract_error_details(content_result)
82+
error_details = utils.extract_error_details(content_result)
8483
error_message = error_details["message"]
8584
error_guid = error_details["guid"]
8685
@@ -94,7 +93,7 @@ if not state.show_instructions:
9493
# ------ DISPLAY SECTION ------ #
9594
# Create the about content text
9695
about_content = f"""<div>
97-
This report uses {current_user_name}'s API key to monitor a single piece of content. It checks whether the content is
96+
This report uses <b>{current_user_name}</b>'s API key to monitor a single piece of content. It checks whether the content is
9897
reachable, but does not verify that it runs without errors.<br><br>
9998
When scheduled to run regularly, the report will send an email alert
10099
if the content becomes unreachable.
@@ -111,15 +110,15 @@ else:
111110
112111
# Create all HTML components up front based on state
113112
html_components = {
114-
'instructions': create_instructions_box(instructions_html) if state.show_instructions else None,
115-
'error': create_error_box(error_guid, error_message) if show_error else None,
116-
'report': create_report_display(content_result, check_time, current_user_name) if content_result and not has_error(content_result) else None,
117-
'no_results': create_no_results_box() if not (state.show_instructions or show_error or
118-
(content_result and not has_error(content_result))) else None
113+
'instructions': utils.create_instructions_box(instructions_html) if state.show_instructions else None,
114+
'error': utils.create_error_box(error_guid, error_message) if show_error else None,
115+
'report': utils.create_report_display(content_result, check_time, current_user_name) if content_result and not utils.has_error(content_result) else None,
116+
'no_results': utils.create_no_results_box() if not (state.show_instructions or show_error or
117+
(content_result and not utils.has_error(content_result))) else None
119118
}
120119
121120
# Always display the About callout box
122-
display(HTML(create_about_box(about_content)))
121+
display(HTML(utils.create_about_box(about_content)))
123122
124123
# Display the first piece of information available from the list of instructions, error, report, etc.
125124
for component in html_components.values():
@@ -128,7 +127,7 @@ for component in html_components.values():
128127
break
129128
130129
# Set send_email variable for the quarto email mechanism
131-
send_email = should_send_email(show_error, content_result)
130+
send_email = utils.should_send_email(show_error, content_result)
132131
```
133132

134133

@@ -147,7 +146,7 @@ send_email = should_send_email(show_error, content_result)
147146
#| echo: false
148147
149148
# Always show the about section in the email
150-
display(HTML(create_about_box(about_content)))
149+
display(HTML(utils.create_about_box(about_content)))
151150
152151
# Display the first piece of information available from the list of instructions, error, report, etc.
153152
for component in html_components.values():

extensions/content-health-monitor/content_health_utils.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def __init__(self):
1818
STATUS_PASS = "PASS"
1919
STATUS_FAIL = "FAIL"
2020
ERROR_PREFIX = "ERROR:"
21+
DEFAULT_USER_NAME = "the publisher" # Default name used if user info cannot be retrieved
2122

2223
# Define CSS styling constants
2324
CSS_COLORS = {
@@ -213,9 +214,8 @@ def get_current_user_full_name(client):
213214

214215
return full_name
215216
except Exception as e:
216-
# Handle any errors gracefully
217-
error_message = format_error_message(e)
218-
print(f"Warning: Could not retrieve current user: {error_message}")
217+
# Handle any errors gracefully without printing
218+
# This prevents errors from appearing at the top of reports
219219
return "Unknown"
220220

221221
# Function to validate content health (simple HTTP 200 check)
@@ -264,9 +264,10 @@ def validate(client, guid, connect_server, api_key):
264264
owner_full_name = f"{owner_first_name} {owner_last_name}".strip()
265265
if not owner_full_name: # Handle case where both names are empty
266266
owner_full_name = "Unknown"
267-
except Exception as e:
268-
# If there's an error getting the owner, keep the defaults
269-
print(f"Warning: Could not retrieve owner for {guid}: {str(e)}")
267+
except Exception:
268+
# If there's an error getting the owner, silently keep the defaults
269+
# No print statement to avoid errors at the top of reports
270+
pass
270271

271272
# Compose URL to logs if we have a dashboard URL, only owner/editor have access
272273
if dashboard_url and content.get("app_role") != "viewer":
@@ -444,7 +445,7 @@ def create_report_display(result_data, check_time_value, current_user_name):
444445
if logs_url:
445446
logs_display = f"<a href='{logs_url}' target='_blank' style='text-decoration:none;'>📋 View Logs</a>"
446447
else:
447-
logs_display = f"Log access is restricted for {current_user_name}. Logs are only available to the content owner and collaborators."
448+
logs_display = f"Log access is restricted for <b>{current_user_name}</b>. Logs are only available to the content owner and collaborators."
448449

449450
# Format owner information
450451
owner_name = result_data.get('owner_name', 'Unknown')

extensions/content-health-monitor/manifest.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"version": "3.11.7",
3333
"package_manager": {
3434
"name": "pip",
35-
"version": "25.1.1",
35+
"version": "24.2",
3636
"package_file": "requirements.txt"
3737
}
3838
},
@@ -41,10 +41,10 @@
4141
"checksum": "5f89d52674b219c0b0ed85f1a5785641"
4242
},
4343
"content-health-monitor.qmd": {
44-
"checksum": "db9599dc24337de8c0becbcefb203371"
44+
"checksum": "f3cb8562a07f411da78748d107ffeb3e"
4545
},
4646
"content_health_utils.py": {
47-
"checksum": "3fc5386653742d8bf625686e486466ea"
47+
"checksum": "0ce8c4e2191fc4763492c5498626912f"
4848
},
4949
"images/refresh-report.png": {
5050
"checksum": "e5680e6188eb8d659e4313cb89d0be3b"

extensions/content-health-monitor/test_content_health_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,7 @@ def test_create_report_display_without_logs_url(self):
10521052
# Assert
10531053
assert html_output is not None
10541054
# Check that the "log access restricted" message is shown with the current user's name
1055-
assert f"Log access is restricted for {current_user_name}" in html_output
1055+
assert f"Log access is restricted for <b>{current_user_name}</b>" in html_output
10561056
assert "only available to the content owner and collaborators" in html_output
10571057
# Ensure there's no logs link
10581058
assert "View Logs</a>" not in html_output

0 commit comments

Comments
 (0)