Improve ASCII art formatting in ticket descriptions

Fixed all box alignment issues and improved visual consistency:

- Standardized box width to 78 chars across all sections
- Unified field width calculations (62 chars for values)
- Fixed executive summary box with proper dynamic width
- Fixed drive specifications box alignment
- Fixed drive timeline box with proper field widths
- Fixed SMART status box and improved temperature handling (None check)
- Fixed SMART attributes box with consistent widths
- Improved partition boxes:
  - 50-char usage meter (2% per block) instead of 20-char
  - Added percentage display next to meter
  - Truncate long mountpoints in header to prevent overflow
  - Consistent field widths across all fields
- Fixed firmware alerts box alignment

All boxes now use consistent Unicode box-drawing characters (┏━┓┃┗┛│)
with proper width calculations for perfect alignment.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-07 19:44:21 -05:00
parent 63daa57d80
commit 88afc8f03e

View File

@@ -990,27 +990,33 @@ class SystemHealthMonitor:
return "🟢 Low - Monitor Only" return "🟢 Low - Monitor Only"
def _generate_detailed_description(self, issue: str, health_report: Dict[str, Any]) -> str: def _generate_detailed_description(self, issue: str, health_report: Dict[str, Any]) -> str:
"""Generate detailed ticket description.""" """Generate detailed ticket description with properly formatted ASCII art."""
hostname = socket.gethostname() hostname = socket.gethostname()
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
priority = "⚠ HIGH" if "CRITICAL" in issue else "● MEDIUM" priority = "⚠ HIGH" if "CRITICAL" in issue else "● MEDIUM"
content_width = self.STANDARD_WIDTH - 2 # Standard box width for all sections (80 chars total)
box_width = 78
field_width = 62 # Width for value fields after label
banner = f""" banner = f"""
{'' * content_width} {'' * box_width}
{' HARDWARE MONITORING ALERT TICKET '.center(content_width)} {' HARDWARE MONITORING ALERT TICKET '.center(box_width)}
{'' * content_width} {'' * box_width}
┃ Host : {hostname:<{content_width-13}} ┃ Host : {hostname:<{box_width - 15}}
┃ Generated : {timestamp:<{content_width-13}} ┃ Generated : {timestamp:<{box_width - 15}}
┃ Priority : {priority:<{content_width-13}} ┃ Priority : {priority:<{box_width - 15}}
{'' * content_width}""" {'' * box_width}"""
issue_type = self._get_issue_type(issue)
impact_level = self._get_impact_level(issue)
executive_summary = f""" executive_summary = f"""
┏━ EXECUTIVE SUMMARY ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┏━ EXECUTIVE SUMMARY {'' * (box_width - 20)}
┃ Issue Type │ {self._get_issue_type(issue)} ┃ Issue Type │ {issue_type:<{field_width}}
┃ Impact Level │ {self._get_impact_level(issue)} ┃ Impact Level │ {impact_level:<{field_width}}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ {'' * box_width}"""
"""
description = banner + executive_summary description = banner + executive_summary
# Add relevant SMART descriptions # Add relevant SMART descriptions
@@ -1042,7 +1048,7 @@ class SystemHealthMonitor:
last_test_date = smart_data.get('last_test_date', 'N/A') last_test_date = smart_data.get('last_test_date', 'N/A')
age = f"{int(power_on_hours/24/365) if isinstance(power_on_hours, (int, float)) else 'N/A'} years" if power_on_hours != 'N/A' else 'N/A' age = f"{int(power_on_hours/24/365) if isinstance(power_on_hours, (int, float)) else 'N/A'} years" if power_on_hours != 'N/A' else 'N/A'
# Fix the formatting issue by ensuring all values are strings and not None # Ensure all values are properly formatted strings
device_safe = device or 'N/A' device_safe = device or 'N/A'
model_safe = drive_details.get('model') or 'N/A' model_safe = drive_details.get('model') or 'N/A'
serial_safe = drive_details.get('serial') or 'N/A' serial_safe = drive_details.get('serial') or 'N/A'
@@ -1051,14 +1057,14 @@ class SystemHealthMonitor:
firmware_safe = drive_details.get('firmware') or 'N/A' firmware_safe = drive_details.get('firmware') or 'N/A'
description += f""" description += f"""
┏━ DRIVE SPECIFICATIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┏━ DRIVE SPECIFICATIONS {'' * (box_width - 23)}
┃ Device Path │ {device_safe:<60} ┃ Device Path │ {device_safe:<{field_width}}
┃ Model │ {model_safe:<60} ┃ Model │ {model_safe:<{field_width}}
┃ Serial │ {serial_safe:<60} ┃ Serial │ {serial_safe:<{field_width}}
┃ Capacity │ {capacity_safe:<60} ┃ Capacity │ {capacity_safe:<{field_width}}
┃ Type │ {type_safe:<60} ┃ Type │ {type_safe:<{field_width}}
┃ Firmware │ {firmware_safe:<60} ┃ Firmware │ {firmware_safe:<{field_width}}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ {'' * box_width}
""" """
if drive_info: if drive_info:
@@ -1074,61 +1080,67 @@ class SystemHealthMonitor:
age_safe = age or 'N/A' age_safe = age or 'N/A'
description += f""" description += f"""
┏━ DRIVE TIMELINE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┏━ DRIVE TIMELINE {'' * (box_width - 17)}
┃ Power-On Hours │ {power_on_safe:<56} ┃ Power-On Hours │ {power_on_safe:<{field_width - 4}}
┃ Last SMART Test │ {last_test_safe:<56} ┃ Last SMART Test │ {last_test_safe:<{field_width - 4}}
┃ Drive Age │ {age_safe:<56} ┃ Drive Age │ {age_safe:<{field_width - 4}}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ {'' * box_width}
""" """
smart_status_safe = drive_info.get('smart_status') or 'N/A' smart_status_safe = drive_info.get('smart_status') or 'N/A'
temp_safe = f"{drive_info.get('temperature')}°C" if drive_info.get('temperature') else 'N/A' # Properly handle temperature with None check
temp_value = drive_info.get('temperature')
temp_safe = f"{temp_value}°C" if temp_value is not None else 'N/A'
description += f""" description += f"""
┏━ SMART STATUS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┏━ SMART STATUS {'' * (box_width - 15)}
┃ Status │ {smart_status_safe:<60} ┃ Status │ {smart_status_safe:<{field_width}}
┃ Temperature │ {temp_safe:<60} ┃ Temperature │ {temp_safe:<{field_width}}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ {'' * box_width}
""" """
if drive_info.get('smart_attributes'): if drive_info.get('smart_attributes'):
description += "\n┏━ SMART ATTRIBUTES " + "" * 48 + "\n" description += f"\n┏━ SMART ATTRIBUTES {'' * (box_width - 19)}\n"
for attr, value in drive_info['smart_attributes'].items(): for attr, value in drive_info['smart_attributes'].items():
attr_safe = str(attr).replace('_', ' ') if attr else 'Unknown' attr_safe = str(attr).replace('_', ' ') if attr else 'Unknown'
value_safe = str(value) if value is not None else 'N/A' value_safe = str(value) if value is not None else 'N/A'
description += f"{attr_safe:<25}{value_safe:<37} \n" description += f"{attr_safe:<27}{value_safe:<{field_width - 14}}\n"
description += "" + "" * 71 + "\n" description += f"{'' * box_width}\n"
if drive_info.get('partitions'): if drive_info.get('partitions'):
for partition in drive_info['partitions']: for partition in drive_info['partitions']:
usage_percent = partition.get('usage_percent', 0) usage_percent = partition.get('usage_percent', 0)
blocks = int(usage_percent / 5) # 20 blocks total = 100% # Create 50-char usage meter (2% per block)
usage_meter = '' * blocks + '' * (20 - blocks) blocks = int(usage_percent / 2)
usage_meter = '' * blocks + '' * (50 - blocks)
mountpoint_safe = partition.get('mountpoint') or 'N/A' mountpoint_safe = partition.get('mountpoint') or 'N/A'
fstype_safe = partition.get('fstype') or 'N/A' fstype_safe = partition.get('fstype') or 'N/A'
total_space_safe = partition.get('total_space') or 'N/A' total_space_safe = partition.get('total_space') or 'N/A'
used_space_safe = partition.get('used_space') or 'N/A' used_space_safe = partition.get('used_space') or 'N/A'
free_space_safe = partition.get('free_space') or 'N/A' free_space_safe = partition.get('free_space') or 'N/A'
usage_pct_str = f"{usage_percent}%"
# Truncate mountpoint if too long for header
mountpoint_display = mountpoint_safe[:50] if len(mountpoint_safe) > 50 else mountpoint_safe
description += f""" description += f"""
┏━ PARTITION [{mountpoint_safe:<60}] ━ ┏━ PARTITION: {mountpoint_display} {'' * (box_width - 14 - len(mountpoint_display))}
┃ Filesystem │ {fstype_safe:<60} ┃ Filesystem │ {fstype_safe:<{field_width}}
┃ Usage Meter │ [{usage_meter:<58}] ┃ Usage Meter │ {usage_meter} {usage_pct_str:>5}
┃ Total Space │ {total_space_safe:<60} ┃ Total Space │ {total_space_safe:<{field_width}}
┃ Used Space │ {used_space_safe:<60} ┃ Used Space │ {used_space_safe:<{field_width}}
┃ Free Space │ {free_space_safe:<60} ┃ Free Space │ {free_space_safe:<{field_width}}
┃ Usage │ {usage_percent}%{'':<57} {'' * box_width}
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
""" """
firmware_info = self._check_disk_firmware(device) firmware_info = self._check_disk_firmware(device)
if firmware_info['is_problematic']: if firmware_info['is_problematic']:
description += "\n┏━ FIRMWARE ALERTS " + "" * 48 + "\n" description += f"\n┏━ FIRMWARE ALERTS {'' * (box_width - 18)}\n"
for issue_item in firmware_info['known_issues']: for issue_item in firmware_info['known_issues']:
issue_safe = str(issue_item) if issue_item else 'Unknown issue' issue_safe = str(issue_item) if issue_item else 'Unknown issue'
description += f"┃ ⚠ {issue_safe:<67} \n" description += f"┃ ⚠ {issue_safe:<{box_width - 5}}\n"
description += "" + "" * 71 + "\n" description += f"{'' * box_width}\n"
except Exception as e: except Exception as e:
description += f"\nError generating drive details: {str(e)}\n" description += f"\nError generating drive details: {str(e)}\n"