
    M"jk                    j   d dl mZ d dlZd dlZd dlmZ d dlmZ d dlmZ  ed          Z	 ee
                                          j        d         Zedz  d	z  Zed
z  Zedz  Zedz  Z ej        d          Zd'dZd(dZd)dZd*dZd+dZd,dZd-d Zd!Zd.d#Zd/d%Zed&k    r e             dS dS )0    )annotationsN)datetime)Path)Anyz8/Users/bot1/.hermes/profiles/it/cron/output/a49833a87c60   workraw_reportsdeliverablesz!profile_audit_dashboard_data.jsonz
index.htmlz{(?i)(secret|token|key|password|access_token|refresh_token|app_secret|verification_token|encrypt_key)(\s*[:=]\s*)([^\s,;`]+)sstrreturnc                :    t                               d |           S )Nc                ^    |                      d          |                      d          z   dz   S )Nr      z
[REDACTED])group)ms       /Users/bot1/Volumes/root_for_ai/AI工作区/通用_HTML看板_Profile记忆异常巡检_20260604_2339/work/generate_dashboard.py<lambda>zredact.<locals>.<lambda>   s$    AGGAJJ$;l$J     )
SECRET_PATsub)r   s    r   redactr      s    >>JJANNNr   line	list[str]c                    d |                                                       d                              d          D             S )Nc                6    g | ]}|                                 S  )strip).0cs     r   
<listcomp>z split_md_row.<locals>.<listcomp>   s     BBB!AGGIIBBBr   |)r   split)r   s    r   split_md_rowr$      s:    BBtzz||11#66<<SAABBBBr   celldict[str, Any]c                ~   |                                  }t          j        d|          rddd|dS t          j        d|          }|sddd|dS t          |                    d                    }|                    d	          p|d
k    rdn	|dk    rdnd}t          |                    d                    |||dS )Nu    (?i)missing|read_error|不存在r   g        missing)charspctstatusrawu5   (\d+)\s*(?:字)?\s*/\s*([\d.]+)%\s*(?:/\s*([^/\s]+))?u   未知r      _   u   临界P   u   高u   正常r   )r   researchfloatr   int)r%   r,   r   r*   r+   s        r   parse_mem_cellr4      s    
**,,C	y4c:: I3)CHHH
 		JCPPA H3(3GGG




CWWQZZXr		HHr		uuxF__SF3OOOr   r3   c                p    	 t          t          j        dd|           pd          S # t          $ r Y dS w xY w)Nz\D+ r   )r3   r0   r   	Exception)r%   s    r   	parse_intr8   ,   sJ    26&"d++0q111   qqs   $' 
55pathr   c                   t          |                     dd                    }d|v r|                    dd          d         n|t          j        d          }|r|                    d          n| j        d d         }t          j        d          }|r'|                    d                                          nd	}t          j        d
          }|r'|                    d                                          nd	}g }                                D ]}	|		                    d          rd|	v sd|	v r t          |	          }
t          |
          dk     rC|
d d         \  }}}}}}}}|dvr[|                    |||t          |          t          |          t          |          t          |          |d           t          d |D                       }t          d |D                       }t          d |D                       }t          d |D                       }t          d |D                       }t          d |D                       }t          d |D                       }t          d |D                       }d(fd}d)d }t!          |           ||||t          |          ||||||||d!v d"
 | |d#d$g                     |d$d%d&g          d'S )*Nutf-8replace)encodingerrorsz## Responser   u@   每日 Profile 记忆与异常巡检报告 - (\d{4}-\d{2}-\d{2})
   z\*\*Run Time:\*\*\s*([^\n]+)r6   u   总览：([^\n]+)r"   z---u   机器   >   bot1bot2)machineprofilegatewayuser_mem
memory_memtimeouts_24h
errors_24hratingc              3  2   K   | ]}|d          dk    dV  dS )rJ      红r   Nr   r   rs     r   	<genexpr>zparse_report.<locals>.<genexpr>S   s0      66A8!5!5a!5!5!5!566r   c              3  2   K   | ]}|d          dk    dV  dS )rJ      黄r   Nr   rM   s     r   rO   zparse_report.<locals>.<genexpr>T   s0      99qAhK5$8$8$8$8$8$899r   c              3  2   K   | ]}|d          dk    dV  dS )rJ      绿r   Nr   rM   s     r   rO   zparse_report.<locals>.<genexpr>U   s0      88a1X;%#7#7#7#7#7#788r   c              3  b   K   | ]*}|d          d         dk    s|d         d         dk    &dV  +dS )rF   r*   r/   rG   r   Nr   rM   s     r   rO   zparse_report.<locals>.<genexpr>V   sJ      aaa
mE&:b&@&@AlOTYDZ^`D`D`1D`D`D`D`aar   c              3  b   K   | ]*}|d          d         dk    s|d         d         dk    &dV  +dS )rF   r*   r.   rG   r   Nr   rM   s     r   rO   zparse_report.<locals>.<genexpr>W   sJ      eeQ!J-*>"*D*D,X]H^bdHdHdqHdHdHdHdeer   c              3  f   K   | ],}t          j        d |d         t           j                  (dV  -dS )u   停止|异常|stale|not runningrE   r   N)r0   r1   IrM   s     r   rO   zparse_report.<locals>.<genexpr>X   sA      kkA3UWXYbWcegei)j)jkakkkkkkr   c              3  &   K   | ]}|d          V  dS )rH   Nr   rM   s     r   rO   zparse_report.<locals>.<genexpr>Y   s'      88a.)888888r   c              3  &   K   | ]}|d          V  dS )rI   Nr   rM   s     r   rO   zparse_report.<locals>.<genexpr>Z   s&      551q555555r   titler   until_titlesr   r   c                                                    }d }t          |          D ]3\  }}|                                                    |           r|dz   } n4|g S g }||d          D ]_}|                                t	          fd|D                       r n-r*                    d          s|                               `|S )Nr   c              3  B   K   | ]}                     |          V  d S )N)
startswith)r   tstrippeds     r   rO   z4parse_report.<locals>.block_after.<locals>.<genexpr>h   s1      @@a8&&q))@@@@@@r   r"   )
splitlines	enumerater   r^   anyappend)	rZ   r[   linesstartir   outr`   texts	          @r   block_afterz!parse_report.<locals>.block_after\   s    !! '' 	 	GAtzz||&&u-- A =I%&&M 	% 	%Dzz||H@@@@<@@@@@  % 3 3C 8 8 %

8$$$
r   re   c                    g }| D ]=}|                     t          j        dd|                                                     >|S )Nz^[-*]\s*r6   )rd   r0   r   r   )re   cleanedr   s      r   clean_anomaly_linesz)parse_report.<locals>.clean_anomaly_linesn   sN     	B 	BD NN26+r488>>@@AAAAr   u   bot2 可达)
profilesredyellowgreenhigh_memcritical_memgateway_badrH   rI   bot2_reachableu   重点异常：u   建议动作：z##z# Cron Job:)source_filereport_daterun_timeoverviewrowssummary	anomaliessuggestions)rZ   r   r[   r   r   r   )re   r   r   r   )r   	read_textr#   r0   r1   r   namer   ra   r^   r$   lenrd   r4   r8   sumr   )r9   raw_text
date_matchrw   	run_matchrx   overview_matchry   rz   r   colsrC   rD   rE   rF   rG   timeoutr>   rJ   ro   rp   rq   rr   rs   rt   total_timeouttotal_errorsrj   rm   ri   s                                @r   parse_reportr   3   s   dnnginHHIIH2?82K2K8>>-++A..QYD^`deeJ)3G*""1%%%3B3K	94@@I-6>yq!!'')))BHY3T::N2@H~##A&&,,...bH!#D!!  s## 	u}}D8H8HD!!t99q==SWXZYZXZS[P'8Z&&***&x00(44%g..#F++	
 	
 		 		 		 		 66666
6
6C99D99999F88488888EaadaaaaaHee$eeeeeLkkkkkkkK88488888M5555555L     $    4yy"D		 (&)&+t3
 
 )(5FIZH[)\)\]]"{#4t]6KLL'  r   rowtuplec                    dddd                     | d         d          }t          | d         d         | d	         d                   }|| d
          | d          | | d         | d         fS )Nr   r   r   )rL   rQ   rS   rJ   r-   rF   r*   rG   rI   rH   rC   rD   )getmax)r   rating_rankmax_mems      r   severity_rankr      sx    Aa0044S]AFFK#j/%(#l*;E*BCCG#l++c..A-AG8SQZ^]`aj]kllr   c                    g } |                      t          t                              d                               t                                          r:|                      t          t                              d                               i }| D ]$}t          |          }|d         r|||d         <   %t          |                                d           }|r|d         ng i g g d}t          |d         t                    |d<   d |D             }t          j
                                        d	          t          t                    t          |          ||t          |d         d
 d          d d         t          |d         d d          d d         t          d |d         D                       dS )Nz*.mdrz   rw   c                    | d         S )Nrw   r   rN   s    r   r   zbuild_data.<locals>.<lambda>   s    Q}5E r   )key)rz   r{   r|   r}   c                2    g | ]}d |d         i|d         S )daterw   r{   r   rM   s     r   r!   zbuild_data.<locals>.<listcomp>   s+    JJJQvq'81Y<8JJJr   z%Y-%m-%d %H:%M:%Sc                R    t          | d         d         | d         d                   S )NrF   r*   rG   )r   r   s    r   r   zbuild_data.<locals>.<lambda>   s&    3q}U?SUVWcUdejUk;l;l r   T)r   reverser?   c                "    | d         | d         fS )NrI   rH   r   r   s    r   r   zbuild_data.<locals>.<lambda>   s    AlOQ~M^;_ r   c                    h | ]
}|d          S )rC   r   rM   s     r   	<setcomp>zbuild_data.<locals>.<setcomp>   s    AAAQAiLAAAr   )generated_at
source_dirreport_countlatesttrends
top_memory
top_errorsmachines)extendsorted
SOURCE_DIRglobRAW_REPORT_DIRexistsr   valuesr   r   nowstrftimer   r   )report_pathsby_dater9   parsedreportsr   r   s          r   
build_datar      s   !Lzv6677888 AF>#6#6v#>#>??@@@)+G 4 4d##&> 	4-3GF=)*W^^%%+E+EFFFG#hWR[["RTeg)h)hFF6N>>>F6NJJ'JJJF //0CDD*ooGVF^1l1lvz{{{|}|  AVF^1_1_imnnnorprorsAA&.AAABB	 	 	r   uFL  <!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="robots" content="noindex,nofollow" />
  <title>每日 Profile 记忆与异常巡检看板</title>
  <style>
    :root{color-scheme:light;--bg:#f7f4ee;--panel:rgba(255,255,255,.78);--ink:#1f2933;--muted:#6b7280;--line:rgba(31,41,51,.10);--green:#27855f;--green-bg:#e6f5ed;--yellow:#b98200;--yellow-bg:#fff0c2;--red:#c6453c;--red-bg:#ffe0dc;--blue:#3765a3;--shadow:0 18px 55px rgba(45,38,24,.10);--radius:22px;--mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono",monospace;--sans:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;--serif:"Songti SC","STSong","Noto Serif CJK SC",Georgia,serif}
    *{box-sizing:border-box}body{margin:0;min-height:100vh;color:var(--ink);background:radial-gradient(circle at top left,rgba(55,101,163,.16),transparent 34rem),radial-gradient(circle at 85% 5%,rgba(198,69,60,.10),transparent 25rem),linear-gradient(180deg,#faf7f0 0%,var(--bg) 55%,#f2ede5 100%);font-family:var(--sans);-webkit-font-smoothing:antialiased}.page{width:min(1440px,calc(100vw - 40px));margin:0 auto;padding:36px 0 56px}.hero{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(360px,.8fr);gap:22px;align-items:stretch;margin-bottom:22px}.hero-main,.panel,.metric,.wide-panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);backdrop-filter:blur(18px)}.hero-main{padding:34px 36px;position:relative;overflow:hidden}.hero-main:after{content:"";position:absolute;inset:auto -90px -130px auto;width:340px;height:340px;background:radial-gradient(circle,rgba(31,41,51,.10),transparent 68%);border-radius:50%;pointer-events:none}.eyebrow{color:var(--blue);font-size:13px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;margin-bottom:18px}h1{font-family:var(--serif);font-weight:650;font-size:clamp(34px,4vw,58px);line-height:1.02;letter-spacing:-.045em;margin:0 0 18px;text-wrap:balance}.subtitle{max-width:840px;color:#4d5968;font-size:16px;line-height:1.75;margin:0}.hero-meta{display:flex;flex-wrap:wrap;gap:10px;margin-top:24px}.pill{display:inline-flex;align-items:center;gap:7px;padding:8px 11px;border-radius:999px;background:rgba(255,255,255,.7);border:1px solid var(--line);color:#4b5563;font-size:12px;font-weight:650}.dot{width:8px;height:8px;border-radius:50%;background:var(--green);display:inline-block}.hero-side{display:grid;grid-template-columns:1fr 1fr;gap:14px}.metric{padding:20px;min-height:132px;display:flex;flex-direction:column;justify-content:space-between}.metric .label{color:var(--muted);font-size:13px;font-weight:650}.metric .value{font-family:var(--serif);font-size:38px;letter-spacing:-.04em;line-height:1;margin-top:18px}.metric .note{color:var(--muted);font-size:12px;margin-top:10px}.metric.red .value{color:var(--red)}.metric.yellow .value{color:var(--yellow)}.metric.blue .value{color:var(--blue)}.metric.green .value{color:var(--green)}.grid{display:grid;grid-template-columns:1fr 1fr;gap:22px;margin-top:22px}.wide-panel,.panel{padding:24px}.panel-title{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;margin-bottom:18px}h2{margin:0;font-size:19px;letter-spacing:-.02em}.hint{color:var(--muted);font-size:12px;line-height:1.5}.chart{width:100%;min-height:240px}.bar-row{display:grid;grid-template-columns:150px minmax(90px,1fr) 72px;gap:12px;align-items:center;margin:13px 0}.bar-label{font-weight:700;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bar-label small{display:block;color:var(--muted);font-weight:550;margin-top:2px}.track{height:12px;border-radius:999px;background:rgba(31,41,51,.08);overflow:hidden}.fill{height:100%;border-radius:999px;background:var(--green);min-width:2px}.fill.high{background:var(--yellow)}.fill.critical{background:var(--red)}.fill.blue{background:var(--blue)}.bar-value{text-align:right;font-family:var(--mono);font-size:12px;color:#374151}.toolbar{display:flex;flex-wrap:wrap;gap:10px;margin:4px 0 18px;align-items:center}.seg{border:1px solid var(--line);background:rgba(255,255,255,.72);border-radius:999px;padding:8px 12px;cursor:pointer;font-size:13px;color:#4b5563}.seg.active{background:#1f2933;color:#fff;border-color:#1f2933}input[type=search]{min-width:260px;flex:1 1 260px;border:1px solid var(--line);border-radius:999px;padding:10px 14px;background:rgba(255,255,255,.78);font:inherit;outline:none}table{width:100%;border-collapse:separate;border-spacing:0 8px}th{text-align:left;color:var(--muted);font-size:12px;font-weight:700;padding:0 10px 6px}td{background:rgba(255,255,255,.76);border-top:1px solid var(--line);border-bottom:1px solid var(--line);padding:12px 10px;vertical-align:middle;font-size:13px}td:first-child{border-left:1px solid var(--line);border-radius:14px 0 0 14px}td:last-child{border-right:1px solid var(--line);border-radius:0 14px 14px 0}.profile-name{font-weight:800;letter-spacing:-.01em}.machine{color:var(--muted);font-size:12px;margin-top:3px}.badge{display:inline-flex;align-items:center;justify-content:center;min-width:36px;padding:4px 9px;border-radius:999px;font-size:12px;font-weight:800}.badge.red{color:var(--red);background:var(--red-bg)}.badge.yellow{color:var(--yellow);background:var(--yellow-bg)}.badge.green{color:var(--green);background:var(--green-bg)}.gateway.bad{color:var(--red);font-weight:750}.gateway.ok{color:var(--green);font-weight:750}.mem-mini{min-width:120px}.mem-line{display:flex;justify-content:space-between;gap:8px;font-family:var(--mono);font-size:11px;color:#4b5563;margin-bottom:5px}.list{margin:0;padding-left:1.15em;color:#3f4855;line-height:1.62;font-size:13px}.list li{margin:6px 0}.footer{color:var(--muted);font-size:12px;margin-top:20px;line-height:1.7}.mono{font-family:var(--mono)}.empty{color:var(--muted);padding:28px;text-align:center;border:1px dashed var(--line);border-radius:16px}@media(max-width:1040px){.hero,.grid{grid-template-columns:1fr}.hero-side{grid-template-columns:repeat(4,1fr)}}@media(max-width:760px){.page{width:min(100vw - 24px,1440px);padding-top:18px}.hero-main{padding:24px}.hero-side{grid-template-columns:1fr 1fr}.grid{gap:14px}.wide-panel,.panel{padding:16px}table{min-width:980px}.table-wrap{overflow:auto}}
  </style>
  <style>
    .priority-panel{margin:0 0 22px;padding:24px}.priority-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:12px}.priority-card{border:1px solid rgba(198,69,60,.18);background:linear-gradient(180deg,rgba(255,224,220,.72),rgba(255,255,255,.72));border-radius:18px;padding:15px}.priority-card h3{margin:0 0 8px;font-size:16px}.priority-card p{margin:0;color:#4b5563;line-height:1.55;font-size:13px}.priority-card .tag{display:inline-flex;margin-bottom:8px;padding:4px 8px;border-radius:999px;background:var(--red-bg);color:var(--red);font-weight:800;font-size:12px}.routine-details{margin-top:14px;border-top:1px solid var(--line);padding-top:12px}.routine-details summary{cursor:pointer;color:#4b5563;font-weight:800}.routine-list{margin:10px 0 0;padding-left:20px;color:#4b5563;line-height:1.6}.routine-list li{margin:3px 0}
  </style>
</head>
<body>
  <main class="page">
    <section class="hero"><div class="hero-main"><div class="eyebrow">Hermes Profiles · Daily Audit</div><h1>每日 Profile 记忆与异常巡检看板</h1><p class="subtitle">把早上 7 点巡检报告转成可视化视图：一眼看出哪些 profile 记忆接近上限、哪些网关异常、哪些 profile 最近 24 小时错误/超时偏高。当前页面为静态 HTML，数据来自 cron 历史报告。</p><div class="hero-meta" id="heroMeta"></div></div><div class="hero-side" id="metricCards"></div></section>
    <section class="wide-panel priority-panel"><div class="panel-title"><div><h2>今日需要关注</h2><div class="hint">只把会明显影响使用体验的高优先级项放在这里：记忆临界、重连/错误集中、gateway 未运行。普通项折叠在下方。</div></div><div class="hint mono" id="priorityCount"></div></div><div id="priorityCards" class="priority-grid"></div><details class="routine-details"><summary>展开普通项 / 低优先级明细</summary><ul id="routineList" class="routine-list"></ul></details></section>
    <section class="grid"><div class="panel"><div class="panel-title"><div><h2>近日报告趋势</h2><div class="hint">红/黄项、临界记忆、错误量的变化。</div></div></div><div id="trendChart" class="chart"></div></div><div class="panel"><div class="panel-title"><div><h2>记忆使用 TOP 10</h2><div class="hint">取 USER 与 MEMORY 两项中的最大使用率。</div></div></div><div id="memoryBars"></div></div></section>
    <section class="grid"><div class="panel"><div class="panel-title"><div><h2>24h 错误 TOP 10</h2><div class="hint">用于优先定位模型/API/网关/工具异常。</div></div></div><div id="errorBars"></div></div><div class="panel"><div class="panel-title"><div><h2>重点异常与建议</h2><div class="hint">保留报告里的红/黄项摘要，敏感字段已脱敏。</div></div></div><div id="adviceBox"></div></div></section>
    <section class="wide-panel" style="margin-top:22px"><div class="panel-title"><div><h2>Profile 明细</h2><div class="hint">默认按风险排序：红项、错误量、超时量、记忆使用率。</div></div><div class="hint mono" id="rowCount"></div></div><div class="toolbar"><button class="seg active" data-filter="all">全部</button><button class="seg" data-filter="红">红</button><button class="seg" data-filter="黄">黄</button><button class="seg" data-filter="绿">绿</button><button class="seg" data-machine="bot1">bot1</button><button class="seg" data-machine="bot2">bot2</button><input type="search" id="search" placeholder="搜索 profile / gateway / 机器…" /></div><div class="table-wrap"><table><thead><tr><th>Profile</th><th>评级</th><th>Gateway</th><th>USER 记忆</th><th>MEMORY 记忆</th><th>24h 超时</th><th>24h 错误</th></tr></thead><tbody id="profileRows"></tbody></table></div></section>
    <div class="footer">数据源：<span class="mono" id="sourceDir"></span><br>说明：这是只读巡检结果的可视化，不会修改 profile、不会重启 gateway、不会清理 memory。要刷新页面，请运行 <span class="mono">python3 work/generate_dashboard.py</span>。</div>
  </main>
<script>
const DATA=__DATA__;
const $=s=>document.querySelector(s);const fmt=new Intl.NumberFormat('zh-CN');const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c]));const ratingClass=r=>r==='红'?'red':r==='黄'?'yellow':'green';const memClass=p=>p>=95?'critical':p>=80?'high':'';const latest=DATA.latest||{summary:{},rows:[]};
function renderHero(){const s=latest.summary||{};$('#heroMeta').innerHTML=[['最新报告',latest.report_date||'-'],['生成时间',DATA.generated_at||'-'],['历史报告',`${DATA.report_count||0} 份`],['bot2',s.bot2_reachable?'可达':'不可达/未知']].map(([k,v],i)=>`<span class="pill"><span class="dot" style="background:${i===3&&!s.bot2_reachable?'var(--red)':'var(--green)'}"></span>${k}：${esc(v)}</span>`).join('');const cards=[['检查 Profile',s.profiles??0,'覆盖 bot1 / bot2','blue'],['红色异常',s.red??0,`黄色 ${s.yellow??0} · 绿色 ${s.green??0}`,'red'],['临界记忆',s.critical_mem??0,`高记忆 ${s.high_mem??0}`,'yellow'],['24h 错误',fmt.format(s.errors_24h??0),`超时 ${fmt.format(s.timeouts_24h??0)} · gateway异常 ${s.gateway_bad??0}`,'green']];$('#metricCards').innerHTML=cards.map(([label,value,note,cls])=>`<div class="metric ${cls}"><div class="label">${label}</div><div><div class="value">${value}</div><div class="note">${note}</div></div></div>`).join('');$('#sourceDir').textContent=DATA.source_dir||''}
function impactReason(r){const parts=[];const u=Number(r.user_mem?.pct||0),m=Number(r.memory_mem?.pct||0),mx=Math.max(u,m);if(mx>=95){parts.push(`${u>=m?'USER':'MEMORY'} 记忆 ${mx.toFixed(1)}%，可能让 profile 变笨/遗忘规则`)}if((r.errors_24h||0)>=300||(r.timeouts_24h||0)>=50){parts.push(`24h 错误 ${fmt.format(r.errors_24h||0)}、超时 ${fmt.format(r.timeouts_24h||0)}，可能导致重连/请求失败`)}if(/停止|异常|stale|not running|stopped|unknown/i.test(r.gateway||'')&&r.profile!=='default'){parts.push(`gateway ${r.gateway}`)}return parts.join('；')||'红/黄项，建议复核'}
function isPriority(r){const mx=Math.max(Number(r.user_mem?.pct||0),Number(r.memory_mem?.pct||0));const gw=/停止|异常|stale|not running|stopped|unknown/i.test(r.gateway||'')&&r.profile!=='default';return mx>=95||(r.errors_24h||0)>=300||(r.timeouts_24h||0)>=50||gw}
function renderPriority(){const rows=latest.rows||[];const priority=rows.filter(isPriority);const routine=rows.filter(r=>!isPriority(r)&&r.rating!=='绿');$('#priorityCount').textContent=`${priority.length} high priority`;$('#priorityCards').innerHTML=priority.slice(0,8).map(r=>`<article class="priority-card"><span class="tag">${esc(r.rating)} · ${esc(r.machine)}</span><h3>${esc(r.profile)}</h3><p>${esc(impactReason(r))}</p></article>`).join('')||'<div class="empty" style="grid-column:1/-1">暂无会明显影响体验的高优先级项。</div>';$('#routineList').innerHTML=routine.slice(0,20).map(r=>`<li><b>${esc(r.machine)}/${esc(r.profile)}</b>：${esc(r.rating)}，USER ${Number(r.user_mem?.pct||0).toFixed(1)}%，MEMORY ${Number(r.memory_mem?.pct||0).toFixed(1)}%，错误 ${fmt.format(r.errors_24h||0)}，超时 ${fmt.format(r.timeouts_24h||0)}</li>`).join('')||'<li>暂无普通红/黄项。</li>'}
function renderTrend(){const t=DATA.trends||[];if(!t.length){$('#trendChart').innerHTML='<div class="empty">暂无历史趋势</div>';return}const w=680,h=245,p={l:42,r:16,t:18,b:42};const maxErr=Math.max(1,...t.map(d=>d.errors_24h||0));const maxRed=Math.max(1,...t.map(d=>d.red||0));const x=i=>p.l+(t.length===1?0:i*(w-p.l-p.r)/(t.length-1));const yErr=v=>p.t+(h-p.t-p.b)*(1-v/maxErr);const yRed=v=>p.t+(h-p.t-p.b)*(1-v/maxRed);const ptsErr=t.map((d,i)=>`${x(i)},${yErr(d.errors_24h||0)}`).join(' ');const ptsRed=t.map((d,i)=>`${x(i)},${yRed(d.red||0)}`).join(' ');const bars=t.map((d,i)=>{const bw=Math.max(6,(w-p.l-p.r)/Math.max(t.length,1)*.36);const bx=x(i)-bw/2,by=yRed(d.critical_mem||0),bh=h-p.b-by;return `<rect x="${bx}" y="${by}" width="${bw}" height="${bh}" rx="4" fill="rgba(185,130,0,.28)"><title>${d.date} 临界记忆 ${d.critical_mem||0}</title></rect>`}).join('');const labels=t.filter((_,i)=>i===0||i===t.length-1||i%3===0).map(d=>`<text x="${x(t.indexOf(d))}" y="${h-16}" text-anchor="middle" fill="#6b7280" font-size="11">${d.date.slice(5)}</text>`).join('');$('#trendChart').innerHTML=`<svg viewBox="0 0 ${w} ${h}" width="100%" height="100%" role="img" aria-label="趋势图"><line x1="${p.l}" x2="${w-p.r}" y1="${h-p.b}" y2="${h-p.b}" stroke="rgba(31,41,51,.12)"/><line x1="${p.l}" x2="${p.l}" y1="${p.t}" y2="${h-p.b}" stroke="rgba(31,41,51,.12)"/>${bars}<polyline points="${ptsErr}" fill="none" stroke="var(--blue)" stroke-width="3" stroke-linejoin="round" stroke-linecap="round"/><polyline points="${ptsRed}" fill="none" stroke="var(--red)" stroke-width="3" stroke-linejoin="round" stroke-linecap="round"/>${t.map((d,i)=>`<circle cx="${x(i)}" cy="${yErr(d.errors_24h||0)}" r="3" fill="var(--blue)"><title>${d.date} 错误 ${d.errors_24h||0}</title></circle><circle cx="${x(i)}" cy="${yRed(d.red||0)}" r="3" fill="var(--red)"><title>${d.date} 红项 ${d.red||0}</title></circle>`).join('')}${labels}<text x="${p.l}" y="14" fill="#6b7280" font-size="11">蓝=24h错误量 · 红=红色profile数 · 黄柱=临界记忆数</text></svg>`}
function barRows(items,type){if(!items?.length)return'<div class="empty">暂无数据</div>';const max=Math.max(1,...items.map(r=>type==='error'?r.errors_24h:Math.max(r.user_mem.pct,r.memory_mem.pct)));return items.map(r=>{const val=type==='error'?r.errors_24h:Math.max(r.user_mem.pct,r.memory_mem.pct);const pct=Math.max(2,val/max*100);const cls=type==='error'?'blue':memClass(val);const sub=type==='error'?`${r.machine} · 超时 ${r.timeouts_24h}`:`${r.machine} · USER ${r.user_mem.pct}% · MEMORY ${r.memory_mem.pct}%`;const show=type==='error'?fmt.format(val):`${val.toFixed(1)}%`;return `<div class="bar-row"><div class="bar-label">${esc(r.profile)}<small>${esc(sub)}</small></div><div class="track"><div class="fill ${cls}" style="width:${pct}%"></div></div><div class="bar-value">${show}</div></div>`}).join('')}
function renderBars(){$('#memoryBars').innerHTML=barRows(DATA.top_memory,'memory');$('#errorBars').innerHTML=barRows(DATA.top_errors,'error')}
function renderAdvice(){const anomalies=(latest.anomalies||[]).filter(x=>!/^[-|]*$/.test(x)).slice(0,14);const suggestions=(latest.suggestions||[]).filter(x=>!/^[-|]*$/.test(x)).slice(0,7);$('#adviceBox').innerHTML=`<div style="display:grid;gap:16px"><div><div class="hint" style="margin-bottom:6px;font-weight:800;color:#374151">重点异常</div><ul class="list">${anomalies.map(x=>`<li>${esc(x)}</li>`).join('')||'<li>暂无重点异常。</li>'}</ul></div><div><div class="hint" style="margin-bottom:6px;font-weight:800;color:#374151">建议动作</div><ul class="list">${suggestions.map(x=>`<li>${esc(x)}</li>`).join('')||'<li>暂无建议。</li>'}</ul></div></div>`}
let filterRating='all',filterMachine='all',query='';function memCell(mem){const pct=Number(mem.pct||0);return `<div class="mem-mini"><div class="mem-line"><span>${mem.chars}字</span><span>${pct.toFixed(1)}%</span></div><div class="track" style="height:7px"><div class="fill ${memClass(pct)}" style="width:${Math.min(100,pct)}%"></div></div></div>`}function renderRows(){const rows=(latest.rows||[]).filter(r=>{const hitRating=filterRating==='all'||r.rating===filterRating;const hitMachine=filterMachine==='all'||r.machine===filterMachine;const txt=`${r.machine} ${r.profile} ${r.gateway} ${r.rating}`.toLowerCase();return hitRating&&hitMachine&&txt.includes(query.toLowerCase())});$('#rowCount').textContent=`${rows.length} / ${(latest.rows||[]).length} rows`;$('#profileRows').innerHTML=rows.map(r=>{const gwBad=/停止|异常|stale|not running/i.test(r.gateway||'');return `<tr><td><div class="profile-name">${esc(r.profile)}</div><div class="machine">${esc(r.machine)}</div></td><td><span class="badge ${ratingClass(r.rating)}">${esc(r.rating)}</span></td><td><span class="gateway ${gwBad?'bad':'ok'}">${esc(r.gateway)}</span></td><td>${memCell(r.user_mem)}</td><td>${memCell(r.memory_mem)}</td><td class="mono">${fmt.format(r.timeouts_24h)}</td><td class="mono">${fmt.format(r.errors_24h)}</td></tr>`}).join('')||`<tr><td colspan="7"><div class="empty">没有匹配的 profile</div></td></tr>`}
function wireFilters(){document.querySelectorAll('.seg').forEach(btn=>btn.addEventListener('click',()=>{if(btn.dataset.filter){filterRating=btn.dataset.filter;document.querySelectorAll('[data-filter]').forEach(b=>b.classList.toggle('active',b.dataset.filter===filterRating))}if(btn.dataset.machine){filterMachine=filterMachine===btn.dataset.machine?'all':btn.dataset.machine;document.querySelectorAll('[data-machine]').forEach(b=>b.classList.toggle('active',filterMachine===b.dataset.machine))}renderRows()}));$('#search').addEventListener('input',e=>{query=e.target.value||'';renderRows()})}
renderHero();renderPriority();renderTrend();renderBars();renderAdvice();renderRows();wireFilters();
</script>
</body>
</html>
datac                X    t          j        | dd                              dd          S )NF),:)ensure_ascii
separatorsz</z<\/)jsondumpsr<   r   s    r   json_for_htmlr      s*    :d:FFFNNtU[\\\r   Nonec                    t                               dd           t                      } t                              t          j        | dd          d           t                              t          	                    dt          |                     d           t          t          j        dt          t                    t          t                    | d	         |                     d
i                               d          |                     d
i                               di                               d          |                     d
i                               di                               d          |                     d
i                               di                               d          dd                     d S )NT)parentsexist_okFr   )r   indentr;   )r=   __DATA__r   r   rw   r{   rn   ro   rs   )okhtmlr   r   latest_datern   ro   rs   )r   )OUT_DIRmkdirr   	DATA_PATH
write_textr   r   	HTML_PATHHTMLr<   r   printr   r   r   s    r   mainr      s   MM$M...<<DDuQGGGRYZZZj-2E2EFFQXYYY	$*II^,xx"--11-@@HHXr**..y"==AA*MMxx"%%)))R88<<UCC2..229bAAEEnUU	 	 	 	 	 	 	 	 	 	r   __main__)r   r   r   r   )r   r   r   r   )r%   r   r   r&   )r%   r   r   r3   )r9   r   r   r&   )r   r&   r   r   )r   r&   )r   r&   r   r   )r   r   )
__future__r   r   r0   r   pathlibr   typingr   r   __file__resolver   PROJECT_DIRr   r   r   r   compiler   r   r$   r4   r8   r   r   r   r   r   r   __name__r   r   r   <module>r      s   " " " " " "  				                  TLMM
d8nn$$&&.q1v%5

&99	l"	RZ  W  X  X
O O O OC C C CP P P P    W W W Wtm m m m   6)X] ] ] ]   " zDFFFFF r   