
    %i$                        U d Z ddlZddlZddlZddlZddlZddlmZ ddlmZm	Z	m
Z
 ddlmZ ddlmZ  e ee      j#                         j$                  j$                        Zeej(                  vrej(                  j+                  de       ddlmZ g dg d	g d
g dg ddZeeee   f   ed<   ddddddZeeef   ed<   dedee   fdZdededz  fdZdededeeef   defdZ deee!f   de!defdZ"d edeeef   fd!Z#d"ed#e!dee   fd$Z$d ed#e!deeef   fd%Z%d&eeef   d'eddfd(Z&d-d)ee   dz  dejN                  fd*Z(d-d)ee   dz  ddfd+Z)e*d,k(  r e)        yy).u  
pattern-detector.py

보고서 디렉토리에서 패턴을 감지하고 팀별로 집계하여
memory/whisper/team-patterns.json에 저장한다.

사용법:
    python3 pattern-detector.py [--workspace /path] [--days 30]

옵션:
    --workspace : 워크스페이스 루트 경로 (기본: /home/jay/workspace)
    --days      : 최근 N일 내 수정된 보고서만 분석 (기본: 30, 0이면 전체)
    N)defaultdict)datetime	timedeltatimezone)Path)Any)extract_task_id_from_filename)u   테스트\s*없u
   test\s*없u   테스트\s*미작성u   테스트\s*SKIPu   테스트\s*생략)u   pyright\s*에러zpyright\s*erroru   타입\s*에러)u   scope\s*초과u   범위\s*초과u   추가\s*작업\s*발생u   예상보다\s*커짐)z	QC\s*FAILzqc_verify.*FAILu+   (?<!없음\s)(?<!없는\s)(?<!\w)FAIL(?!\w))u   회귀(?!\s*없)u   (?<!\w)regression(?!\s*없)u   기존\s*테스트\s*실패)test_missingpyright_errorscope_exceededqc_fail
regressionPATTERN_KEYWORDSg333333?g      ?g?g333333?g?)r   r
   r   r   r   PATTERN_WEIGHTScontentreturnc                     g }t         j                         D ]D  \  }}|D ]:  }t        j                  || t        j                        s)|j                  |        D F |S )u~   
    보고서 내용에서 패턴 타입 목록을 반환한다.
    동일 패턴은 한 번만 반환 (중복 제거).
    )r   itemsresearch
IGNORECASEappend)r   foundpattern_typekeywordskws        I/home/jay/workspace/.worktrees/task-2487-dev2/scripts/pattern-detector.pydetect_patternsr   O   s_    
 E"2"8"8": h 	ByyWbmm4\*	
 L    filenamec                 "    t        |       }||S y)u   
    파일명에서 task-id를 추출한다.
    예: "task-100_1.2.md" → "task-100_1.2"
    매칭 실패 시 None 반환.
    utils.task_id_parser.extract_task_id_from_filename 위임 (V2 호환).
    N)_extract_task_id_from_filename)r    results     r   r	   r	   ]   s     ,H5Fr   tasksc                 <   g d}|D ]`  }t        j                  || t         j                  t         j                  z        }|s<|j	                  d      j                         }|s^|c S  t        |      }|r&||v r"||   j                  dd      }|rt        |      S y)u   
    보고서 내용 또는 task-timers.json에서 팀 이름을 추출한다.
    우선순위: 보고서 내용 > task-timers.json > "unknown"
    )u   \*\*팀:\*\*\s*([\w-]+)u   ^팀:\s*([\w-]+)z^team:\s*([\w-]+)z^team_id:\s*([\w-]+)u   팀:\s*([\w-]+)zteam:\s*([\w-]+)zteam_id:\s*([\w-]+)   team_id unknown)	r   r   r   	MULTILINEgroupstripr	   getstr)	r   r    r$   team_patternstpmteamtask_idtids	            r   extract_team_from_contentr5   j   s    M  IIb'2==2<<#?@771:##%D ,H5G7e#Gn  B/s8Or   pattern_countstotal_reportsc                 f     |dk(  ryt         fdt        j                         D              }||z  S )uc   
    risk_score = sum(count * weight) / total_reports
    total_reports가 0이면 0.0 반환.
    r   g        c              3   N   K   | ]  \  }}j                  |d       |z    yw)r   N)r-   ).0ptypeweightr6   s      r   	<genexpr>z'calculate_risk_score.<locals>.<genexpr>   s'     c-%""5!,v5cs   "%)sumr   r   )r6   r7   totals   `  r   calculate_risk_scorer@      s4     c?K`K`KbccE=  r   	workspacec                     t        |       dz  dz  }	 |j                  d      }t        j                  |      }|j	                  di       S # t
        $ r i cY S w xY w)u=   task-timers.json을 로드한다. 실패 시 빈 dict 반환.memoryztask-timers.jsonutf-8encodingr$   )r   	read_textjsonloadsr-   	Exception)rA   pathtextdatas       r   load_task_timersrN      s^    	?X%(::D~~w~/zz$xx$$ 	s   8A AAreports_dirdaysc                    | j                         sg S t        | j                  d            }|dkD  rt        j                  t
        j                        t        |      z
  }g }|D ]V  }	 t        j                  |j                         j                  t
        j                        }||k\  r|j                  |       X |S |S # t        $ r Y hw xY w)u   
    보고서 디렉토리에서 .md 파일을 수집한다.
    days > 0이면 최근 days일 내 수정된 파일만 반환.
    days == 0이면 전체 반환.
    z*.mdr   tz)rP   )existslistglobr   nowr   utcr   fromtimestampstatst_mtimer   rJ   )rO   rP   filescutofffilteredfmtimes          r   collect_report_filesra      s     	!!&)*Eax.1EE! 	A ..qvvx/@/@X\\RF?OOA&		 L	  s   +AC	CCc           
         t        |       }t        |       dz  dz  }t        ||      }t        d       }d}|D ]\  }	 |j	                  d      }|dz  }|j                  }	t        ||	|      }
t        |      }|D ]  }||
   |   j                  |	        ^ i |j                         D ]  \  }
}|j                         D ci c]  \  }}|t        |       }}}t        ||      }t        |j                               D cg c]  \  }}|t        |      t        |      d }}}|t        |d	      d
|
<    t        d j!                         D              }d}rt#        j%                         fd      }dt'        j(                  t*        j,                        j/                  d      |||dd}|S # t
        $ r Y w xY wc c}}w c c}}w )uT   
    보고서 디렉토리를 분석하여 패턴 결과 dict를 반환한다.
    rC   reportsc                       t        t              S N)r   rU    r   r   <lambda>z!analyze_reports.<locals>.<lambda>   s    kZ^N_ r   r   rD   rE   r&   )typecountrecent_reports   )patterns
risk_scorec              3   :   K   | ]  }|d    D ]	  }|d      yw)rl   ri   Nrf   )r:   	team_dataps      r   r=   z"analyze_reports.<locals>.<genexpr>   s'     j	T]^hTijq7jjs   r(   c                     |    d   S )Nrm   rf   )tteams_results    r   rg   z!analyze_reports.<locals>.<lambda>   s    ,q/,7 r   )keyrR   z%Y-%m-%dT%H:%M:%S)total_reports_analyzedtotal_patterns_foundhighest_risk_team)versionlast_updatedteamssummary)rN   r   ra   r   rG   rJ   namer5   r   r   r   lenr@   sortedroundr>   valuesmaxkeysr   rW   r   rX   strftime)rA   rP   r$   rO   report_filesteam_pattern_filestotal_analyzedreport_pathr   r    r2   rl   r;   pattern_mapr\   r6   riskpatterns_listtotal_patternsrw   r#   rs   s                        @r   analyze_reportsr      s    Y'Ey/H,y8K'T:L ;FF_:`N# =	!++W+=G 	!##((EB"7+ 	=Et$U+228<	== $&L/557 
k@K@Q@Q@STu%U+TT#NNC !'{'8'8': ;
 u	 U"(-
 
 &a.
T
& j\5H5H5JjjN 7
  5>>?RS&4$2!2
		F Mk  		 U
s   F>G"G>	G
Gr#   output_pathc                     t        |      }|j                  j                  dd       |j                  t	        j
                  | dd      d       y)	uN   결과를 JSON 파일로 저장한다. 디렉토리가 없으면 생성한다.T)parentsexist_okF   ensure_asciiindentrD   rE   N)r   parentmkdir
write_textrH   dumps)r#   r   rK   s      r   save_resultsr   	  sG    DKKdT2OO

6a8  r   argvc                 V   t        j                  d      }|j                  dt        j                  j                  dt        t        t              j                         j                  j                              d       |j                  dt        dd	
       |j                  |       S )NuE   보고서에서 세션 패턴을 감지하고 팀별로 집계한다.)descriptionz--workspaceWORKSPACE_ROOTuU   워크스페이스 루트 경로 (기본: $WORKSPACE_ROOT 또는 /home/jay/workspace))defaulthelpz--days   uJ   최근 N일 내 수정된 보고서만 분석 (기본: 30, 0이면 전체))rh   r   r   )argparseArgumentParseradd_argumentosenvironr-   r.   r   __file__resolver   int
parse_args)r   parsers     r   r   r     s    $$1xyF


/T(^5K5K5M5T5T5[5[1\]d  
 Y	   T""r   c                    t        |       }t        |j                  |j                        }t	        t        |j                        dz  dz  dz        }t        ||       t        t        j                  |dd             y )N)rA   rP   rC   whisperzteam-patterns.jsonFr   r   )
r   r   rA   rP   r.   r   r   printrH   r   )r   argsr#   r   s       r   mainr   (  sb    dDt~~DIIFFd4>>*X5	ADXXYK%	$**V%
:;r   __main__re   )+__doc__r   rH   r   r   syscollectionsr   r   r   r   pathlibr   typingr   r.   r   r   r   
_UTILS_DIRrK   insertutils.task_id_parserr	   r"   r   dictrU   __annotations__r   floatr   r5   r   r@   rN   ra   r   r   	Namespacer   r   __name__rf   r   r   <module>r      s'     	 	 
 # 2 2  h'')00778
SXXHHOOAz" `

1* $sDI~& B %c5j! S T#Y 
C 
C$J 
""" S>" 		"J!cN!! ! S#X d # $t* 4Ds D# D$sCx. DNc3h c d #T#Y% #1C1C # <tCy4 <4 < zF r   