
    ix#                        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 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j>                  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)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-2116-dev1/scripts/pattern-detector.pydetect_patternsr   J   s_    
 E"2"8"8": h 	ByyWbmm4\*	
 L    filenamec                 V    t        j                  d|       }|r|j                  d      S y)u}   
    파일명에서 task-id를 추출한다.
    예: "task-100.1.md" → "task-100"
    매칭 실패 시 None 반환.
    z^(task-\d+)   N)r   matchgroup)r   ms     r   extract_task_id_from_filenamer%   X   s(     	*Awwqz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-]+)r!   team_id unknown)	r   r   r   	MULTILINEr#   stripr%   getstr)	r   r   r&   team_patternstpr$   teamtask_idtids	            r   extract_team_from_contentr4   d   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weightr5   s      r   	<genexpr>z'calculate_risk_score.<locals>.<genexpr>   s'     c-%""5!,v5cs   "%)sumr   r   )r5   r6   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)r@   pathtextdatas       r   load_task_timersrM      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)rO   )existslistglobr   nowr   utcr   fromtimestampstatst_mtimer   rI   )rN   rO   filescutofffilteredfmtimes          r   collect_report_filesr`      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를 반환한다.
    rB   reportsc                       t        t              S N)r   rT    r   r   <lambda>z!analyze_reports.<locals>.<lambda>   s    kZ^N_ r   r   rC   rD   r!   )typecountrecent_reports   )patterns
risk_scorec              3   :   K   | ]  }|d    D ]	  }|d      yw)rk   rh   Nre   )r9   	team_dataps      r   r<   z"analyze_reports.<locals>.<genexpr>   s'     j	T]^hTijq7jjs   r)   c                     |    d   S )Nrl   re   )tteams_results    r   rf   z!analyze_reports.<locals>.<lambda>   s    ,q/,7 r   )keyrQ   z%Y-%m-%dT%H:%M:%S)total_reports_analyzedtotal_patterns_foundhighest_risk_team)versionlast_updatedteamssummary)rM   r   r`   r   rF   rI   namer4   r   r   r   lenr?   sortedroundr=   valuesmaxkeysr   rV   r   rW   strftime)r@   rO   r&   rN   report_filesteam_pattern_filestotal_analyzedreport_pathr   r   r1   rk   r:   pattern_mapr[   r5   riskpatterns_listtotal_patternsrv   resultrr   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indentrC   rD   N)r   parentmkdir
write_textrG   dumps)r   r   rJ   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이면 전체))rg   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)r@   rO   rB   whisperzteam-patterns.jsonFr   r   )
r   r   r@   rO   r.   r   r   printrG   r   )r   argsr   r   s       r   mainr   "  sb    dDt~~DIIFFd4>>*X5	ADXXYK%	$**V%
:;r   __main__rd   )#__doc__r   rG   r   r   syscollectionsr   r   r   r   pathlibr   typingr   r   dictr.   rT   __annotations__r   floatr   r%   r4   r   r?   rM   r`   r   r   	Namespacer   r   __name__re   r   r   <module>r      s     	 	 
 # 2 2  

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   