|
|
import pandas as pd |
|
|
|
|
|
def get_verdict_badge(verdict, status, github_state): |
|
|
v = str(verdict).lower() |
|
|
|
|
|
if status == 'executed' or github_state == 'closed': |
|
|
return '<span style="background-color: #374151; color: white; padding: 4px 8px; border-radius: 12px; font-size: 10px; font-weight: 600;">CLOSED</span>' |
|
|
elif "possibly_resolved" in v: |
|
|
return '<span style="background-color: #84CC16; color: white; padding: 4px 8px; border-radius: 12px; font-size: 10px; font-weight: 600; white-space: nowrap;">POSSIBLY RESOLVED</span>' |
|
|
elif "unresolved" in v: |
|
|
return '<span style="background-color: #64748B; color: white; padding: 4px 8px; border-radius: 12px; font-size: 10px; font-weight: 600;">OPEN BUG</span>' |
|
|
elif "resolved" in v: |
|
|
return '<span style="background-color: #10B981; color: white; padding: 4px 8px; border-radius: 12px; font-size: 10px; font-weight: 600;">RESOLVED</span>' |
|
|
elif "duplicate" in v: |
|
|
return '<span style="background-color: #F59E0B; color: white; padding: 4px 8px; border-radius: 12px; font-size: 10px; font-weight: 600;">DUPLICATE</span>' |
|
|
elif "pending" in v or "new" in v: |
|
|
|
|
|
return '<span style="background-color: #94A3B8; color: white; padding: 3px 7px; border-radius: 12px; font-size: 10px; font-weight: 500; white-space: nowrap;">WAITING AGENT</span>' |
|
|
else: |
|
|
return f'<span style="background-color: #475569; color: white; padding: 4px 8px; border-radius: 12px; font-size: 10px;">{verdict}</span>' |
|
|
|
|
|
def get_priority_badge(priority): |
|
|
p = str(priority).lower() if priority else "" |
|
|
|
|
|
if "critical" in p: return '<span style="color: #EF4444; font-weight: 700;">🔥 Critical</span>' |
|
|
if "high" in p: return '<span style="color: #F97316; font-weight: 600;">High</span>' |
|
|
if "medium" in p: return '<span style="color: #3B82F6;">Medium</span>' |
|
|
if "low" in p: return '<span style="color: #9CA3AF;">Low</span>' |
|
|
return "-" |
|
|
|
|
|
def generate_issues_html(df: pd.DataFrame, sort_col: str = "updated_at", sort_asc: bool = False) -> str: |
|
|
if df.empty: |
|
|
|
|
|
return """ |
|
|
<div style='padding: 40px; text-align: center; color: var(--body-text-color); background: var(--background-fill-primary); border-radius: 12px; border: 1px solid #E2E8F0; font-family: sans-serif;'> |
|
|
No issues found for this view. |
|
|
</div> |
|
|
""" |
|
|
|
|
|
headers_map = { |
|
|
"Issue": "issue_number", |
|
|
"Title / Repo": "title", |
|
|
"Verdict": "verdict", |
|
|
|
|
|
"Model": "llm_model", |
|
|
|
|
|
"Updated": "updated_at" |
|
|
} |
|
|
|
|
|
html = """ |
|
|
<style> |
|
|
.tm-table-container { |
|
|
background-color: var(--background-fill-primary) !important; |
|
|
border: 0px solid var(--border-color-primary) !important; |
|
|
border-radius: 6px; |
|
|
max-height: 500px; |
|
|
overflow-y: auto; |
|
|
position: relative; |
|
|
font-family: 'Inter', sans-serif; |
|
|
} |
|
|
|
|
|
.tm-table { |
|
|
width: 100%; |
|
|
border-collapse: collapse; |
|
|
border-spacing: 0; |
|
|
border: none !important; /* Remove table border */ |
|
|
background-color: transparent !important; |
|
|
color: var(--body-text-color) !important; |
|
|
} |
|
|
|
|
|
/* HEADER */ |
|
|
.tm-table thead th { |
|
|
position: sticky; |
|
|
top: 0; |
|
|
background: linear-gradient(135deg, var(--primary-600) 0%, var(--primary-500) 100%) !important; |
|
|
color: #FFFFFF !important; |
|
|
z-index: 10; |
|
|
padding: 14px 16px; |
|
|
text-align: left; |
|
|
font-weight: 600; |
|
|
text-transform: uppercase; |
|
|
font-size: 11px; |
|
|
letter-spacing: 0.05em; |
|
|
border: none !important; /* Remove header bottom border */ |
|
|
box-sizing: content-box !important; /* Requested adjustment */ |
|
|
} |
|
|
|
|
|
/* ROWS */ |
|
|
.tm-table tbody tr { |
|
|
background-color: var(--background-fill-primary) !important; |
|
|
border: none !important; /* Remove border between rows */ |
|
|
cursor: pointer; |
|
|
transition: all 0.15s; |
|
|
box-sizing: content-box !important; /* Requested adjustment */ |
|
|
} |
|
|
|
|
|
/* Zebra Striping (Essential now that there are no borders) */ |
|
|
.tm-table tbody tr:nth-child(even) { |
|
|
background-color: var(--background-fill-secondary) !important; |
|
|
} |
|
|
|
|
|
/* Hover */ |
|
|
.tm-table tbody tr:hover { |
|
|
background-color: var(--background-fill-secondary) !important; |
|
|
filter: brightness(1.1); /* Subtle highlight */ |
|
|
} |
|
|
|
|
|
/* CELLS */ |
|
|
.tm-table td { |
|
|
padding: 14px 16px; |
|
|
color: var(--body-text-color) !important; |
|
|
font-size: 13px; |
|
|
vertical-align: middle; |
|
|
border: none !important; /* Ensure no borders in cells */ |
|
|
} |
|
|
|
|
|
/* Typography */ |
|
|
.tm-title-text { |
|
|
font-weight: 600; |
|
|
color: var(--body-text-color) !important; |
|
|
font-size: 14px; |
|
|
} |
|
|
.tm-subtext { |
|
|
font-size: 11px; |
|
|
color: var(--body-text-color-subdued) !important; |
|
|
margin-top: 2px; |
|
|
} |
|
|
|
|
|
/* Sort Icons */ |
|
|
.tm-sortable { cursor: pointer; } |
|
|
.tm-sort-icon { display: inline-block; margin-left: 6px; font-size: 9px; color: rgba(255,255,255,0.7) !important; } |
|
|
.tm-sort-active { color: #FFFFFF !important; } |
|
|
|
|
|
/* Status (Keep only the left border for indication) */ |
|
|
.status-pending { border-left: 4px solid #F59E0B !important; } |
|
|
.status-executed { border-left: 4px solid #10B981 !important; } |
|
|
.status-new { border-left: 4px solid transparent !important; } |
|
|
|
|
|
/* View Button */ |
|
|
a.tm-view-btn { |
|
|
background-color: transparent !important; |
|
|
color: #3B82F6 !important; |
|
|
border: 1px solid #3B82F6 !important; |
|
|
padding: 5px 10px; |
|
|
border-radius: 20px; |
|
|
font-size: 10px; |
|
|
font-weight: 700; |
|
|
text-transform: uppercase; |
|
|
text-decoration: none; |
|
|
display: inline-block; |
|
|
transition: all 0.2s; |
|
|
} |
|
|
a.tm-view-btn:hover { |
|
|
background-color: #3B82F6 !important; |
|
|
color: #FFFFFF !important; |
|
|
} |
|
|
</style> |
|
|
|
|
|
<div class="tm-table-container"> |
|
|
<table class="tm-table"> |
|
|
<thead> |
|
|
<tr> |
|
|
""" |
|
|
|
|
|
|
|
|
for display, col_name in headers_map.items(): |
|
|
width = 'style="width: 70px;"' if display == "Issue" else "" |
|
|
|
|
|
icon = "" |
|
|
if col_name == sort_col: |
|
|
icon = "▲" if sort_asc else "▼" |
|
|
icon_class = "tm-sort-icon tm-sort-active" |
|
|
else: |
|
|
icon = "▼" |
|
|
icon_class = "tm-sort-icon" |
|
|
|
|
|
html += f""" |
|
|
<th class="tm-sortable" {width} data-sort-col="{col_name}" title="Sort by {display}"> |
|
|
{display} <span class="{icon_class}">{icon}</span> |
|
|
</th> |
|
|
""" |
|
|
|
|
|
html += """ |
|
|
<th style="width: 90px; text-align: center;">Actions</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
""" |
|
|
|
|
|
for _, row in df.iterrows(): |
|
|
issue_num = row['issue_number'] |
|
|
repo_full = str(row['repo_url']) |
|
|
repo_short = repo_full.split('github.com/')[-1] if 'github.com' in repo_full else repo_full |
|
|
|
|
|
title = str(row.get('title') or "No Title") |
|
|
verdict_html = get_verdict_badge(row['verdict'], row['status'], row.get('github_state')) |
|
|
|
|
|
|
|
|
raw_model = str(row.get('llm_model') or "") |
|
|
model = raw_model.split('/')[-1] if raw_model else "-" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
status_code = row.get('status', 'new') |
|
|
status_class = f"status-{status_code}" if status_code in ['pending_approval', 'executed'] else "status-new" |
|
|
|
|
|
data_attrs = f'data-issue-number="{issue_num}" data-repo-url="{row["repo_url"]}" data-verdict="{row["verdict"]}"' |
|
|
target_url = f"{repo_full.rstrip('/')}/issues/{issue_num}" |
|
|
|
|
|
html += f""" |
|
|
<tr class="{status_class}" {data_attrs}> |
|
|
<td style="font-family: 'Monaco', monospace; font-weight: 600; color: #475569;">#{issue_num}</td> |
|
|
<td> |
|
|
<div class="tm-title-text">{title[:65]}{'...' if len(title)>65 else ''}</div> |
|
|
<div class="tm-subtext">{repo_short}</div> |
|
|
</td> |
|
|
<td>{verdict_html}</td> |
|
|
<td class="tm-subtext">{model}</td> |
|
|
<td class="tm-subtext">{str(row["updated_at"])[:10]}</td> |
|
|
<td style="text-align: center;"> |
|
|
<a href="{target_url}" target="_blank" class="tm-view-btn" onclick="event.stopPropagation()"> |
|
|
GitHub ↗ |
|
|
</a> |
|
|
</td> |
|
|
</tr> |
|
|
""" |
|
|
|
|
|
html += "</tbody></table></div>" |
|
|
return html |