1- import sessionData from "../data/session/session_6.json" with { type : "json" } ;
21import { MEMBERS , STUDY_CONFIG } from "./utils/constants.js" ;
32import { getKSTDateString } from "./utils/date.js" ;
3+ import { getLatestSessionData } from "./utils/session.js" ;
44import { createMarkdownTable } from "./utils/formatter.js" ;
55import {
66 createDiscussion ,
@@ -10,73 +10,68 @@ import {
1010 addLabelByName ,
1111} from "./utils/github.js" ;
1212
13- export default async ( { github, context, core } ) => {
13+ export default async ( { github, context, core, test } ) => {
1414 const { RULES } = STUDY_CONFIG ;
1515 const { MIN_REVIEWS_REQUIRED } = RULES ;
1616
1717 try {
18- const nowStrDots = getKSTDateString ( new Date ( ) ) ;
19- const sessionStart = new Date ( sessionData . date . start ) ;
20- const sessionEnd = new Date ( sessionData . date . end ) ;
21- const weeks = sessionData . challenges . map ( ( c ) => c . week ) ;
18+ const sessionData = getLatestSessionData ( ) ;
19+ const nowStr = getKSTDateString ( new Date ( ) ) ;
20+ const sessionEnd = getKSTDateString ( new Date ( sessionData . date . end ) ) ;
2221
23- if (
24- nowStrDots !== getKSTDateString ( sessionEnd ) &&
25- context . eventName !== "workflow_dispatch"
26- ) {
22+ if ( test ) {
23+ console . log ( "[테스트 모드] 날짜 체크를 건너뜁니다." ) ;
24+ } else if ( nowStr !== sessionEnd ) {
2725 console . warn (
28- `오늘은 세션 종료일(${ getKSTDateString ( sessionEnd ) } )이 아닙니다. 현재 날짜: ${ nowStrDots } . 세션 리포트 생성을 스킵합니다.` ,
26+ `오늘은 세션 종료일(${ sessionEnd } )이 아닙니다. 현재 날짜: ${ nowStr } . 세션 리포트 생성을 스킵합니다.` ,
2927 ) ;
3028 return ;
3129 }
3230
3331 console . log (
34- `오늘은 세션 ${ sessionData . id } 종료일 (${ sessionData . date . start } ~ ${ sessionData . date . end } ): 세션 ${ sessionData . id } 리포트 작성을 시작합니다. ` ,
32+ `세션 ${ sessionData . id } 리포트 작성 시작 (${ sessionData . date . start } ~ ${ sessionData . date . end } )` ,
3533 ) ;
3634
37- const repository = await getRepositoryInfo ( { github, context } ) ;
38- const repoId = repository . id ;
39- const discussionCategories = await getDiscussionCategories ( {
40- github,
41- context,
35+ const sessionStart = new Date ( sessionData . date . start ) ;
36+ const sessionEndDate = new Date ( sessionData . date . end ) ;
37+ const weeks = sessionData . challenges . map ( ( c ) => c . week ) ;
38+ const firstWeek = weeks [ 0 ] ;
39+
40+ // ─── 출석 정보 (세션 레벨 absentees) ───
41+ const absenteeMap = { } ;
42+ ( sessionData . absentees || [ ] ) . forEach ( ( a ) => {
43+ if ( a . name ) {
44+ absenteeMap [ a . name ] = a . status ;
45+ }
4246 } ) ;
43- const categoryReport = discussionCategories . find ( ( cat ) =>
44- cat . name . toLowerCase ( ) . includes ( "report" ) ,
45- ) ;
4647
48+ // ─── 멤버 데이터 초기화 ───
4749 const reportData = { } ;
48-
4950 MEMBERS . forEach ( ( member ) => {
50- const githubId = member . githubId ;
51-
52- reportData [ githubId ] = {
51+ reportData [ member . githubId ] = {
5352 name : member . name ,
5453 discordId : member . discordId ,
5554 weeks : { } ,
5655 totalPRs : 0 ,
5756 totalReviews : 0 ,
5857 } ;
5958
60- sessionData . challenges . forEach ( ( challenge ) => {
61- const w = challenge . week ;
62- const absentees = challenge . absentees || [ ] ;
63- const isAbsent = absentees . includes ( githubId ) ;
64-
65- reportData [ githubId ] . weeks [ w ] = {
59+ weeks . forEach ( ( w ) => {
60+ reportData [ member . githubId ] . weeks [ w ] = {
6661 pr : false ,
62+ prUrl : "" ,
6763 reviews : 0 ,
68- isAbsent : isAbsent ,
6964 } ;
7065 } ) ;
7166 } ) ;
7267
68+ // ─── PR + 리뷰 집계 ───
7369 const sessionPRs = await getThisWeekPRs ( {
7470 github,
7571 context,
7672 startDate : sessionStart ,
77- endDate : sessionEnd ,
73+ endDate : sessionEndDate ,
7874 } ) ;
79-
8075 console . log ( `세션 기간 내 PR 개수 = ${ sessionPRs . length } ` ) ;
8176
8277 await Promise . all (
@@ -93,6 +88,7 @@ export default async ({ github, context, core }) => {
9388
9489 const weekNum = weekInfo . week ;
9590 reportData [ author ] . weeks [ weekNum ] . pr = true ;
91+ reportData [ author ] . weeks [ weekNum ] . prUrl = pr . html_url ;
9692 reportData [ author ] . totalPRs ++ ;
9793
9894 const { data : reviews } = await github . rest . pulls . listReviews ( {
@@ -111,54 +107,103 @@ export default async ({ github, context, core }) => {
111107 } ) ,
112108 ) ;
113109
114- const tableConfig = {
115- headers : [ "이름" , ...weeks . map ( ( w ) => `W${ w } ` ) , "총합" ] ,
116- paddings : [ 6 , ...weeks . map ( ( ) => 15 ) , 10 ] ,
110+ // ─── 활동 테이블 (5주씩 분할) ───
111+ const memberIds = MEMBERS . map ( ( m ) => m . githubId ) ;
112+ const CHUNK_SIZE = 5 ;
113+ const weekChunks = [ ] ;
114+ for ( let i = 0 ; i < weeks . length ; i += CHUNK_SIZE ) {
115+ weekChunks . push ( weeks . slice ( i , i + CHUNK_SIZE ) ) ;
116+ }
117+
118+ const activityTables = weekChunks . map ( ( chunk ) => {
119+ const tableConfig = {
120+ headers : [ "이름" , ...chunk . map ( ( w ) => `W${ w } ` ) ] ,
121+ paddings : [ 6 , ...chunk . map ( ( ) => 20 ) ] ,
122+ renderRow : ( id ) => {
123+ const s = reportData [ id ] ;
124+ const row = { name : s . name } ;
125+
126+ chunk . forEach ( ( w ) => {
127+ const wData = s . weeks [ w ] ;
128+ const parts = [ ] ;
129+
130+ if ( w === firstWeek ) {
131+ const absentStatus = absenteeMap [ s . name ] ;
132+ if ( absentStatus === "absent" ) {
133+ parts . push ( "❌결석" ) ;
134+ } else if ( absentStatus === "late" ) {
135+ parts . push ( "⏰지각" ) ;
136+ } else {
137+ parts . push ( "✅출석" ) ;
138+ }
139+ }
140+
141+ if ( wData . pr ) {
142+ parts . push ( `✅[PR](${ wData . prUrl } )` ) ;
143+ } else {
144+ parts . push ( "❌PR" ) ;
145+ }
146+
147+ if ( wData . reviews >= MIN_REVIEWS_REQUIRED ) {
148+ parts . push ( `✅리뷰${ wData . reviews } /${ MIN_REVIEWS_REQUIRED } ` ) ;
149+ } else {
150+ parts . push ( `❌리뷰${ wData . reviews } /${ MIN_REVIEWS_REQUIRED } ` ) ;
151+ }
152+
153+ row [ `week${ w } ` ] = parts . join ( " " ) ;
154+ } ) ;
155+
156+ return row ;
157+ } ,
158+ } ;
159+
160+ return createMarkdownTable ( memberIds , tableConfig ) ;
161+ } ) ;
162+
163+ // ─── 총합 테이블 ───
164+ const summaryTableConfig = {
165+ headers : [ "이름" , "총 PR" , "총 리뷰" ] ,
166+ paddings : [ 6 , 8 , 8 ] ,
117167 renderRow : ( id ) => {
118168 const s = reportData [ id ] ;
119- const row = { name : s . name } ;
120- let attendanceCount = 0 ;
121-
122- weeks . forEach ( ( w ) => {
123- const wData = s . weeks [ w ] ;
124- const hasPR = wData . pr ;
125- const hasRev = wData . reviews >= MIN_REVIEWS_REQUIRED ;
126- const isAbsent = wData . isAbsent ;
127- const isSuccess = hasPR && hasRev && ! isAbsent ;
128-
129- if ( isSuccess ) {
130- row [ `week${ w } ` ] = "✅" ;
131- attendanceCount ++ ;
132- } else {
133- const reasons = [ ] ;
134-
135- if ( ! hasPR ) reasons . push ( "PR" ) ;
136- if ( ! hasRev )
137- reasons . push ( `리뷰${ wData . reviews } /${ MIN_REVIEWS_REQUIRED } ` ) ;
138- if ( isAbsent ) reasons . push ( "결석" ) ;
139-
140- const reasonText =
141- reasons . length > 0 ? `(${ reasons . join ( "," ) } )` : "" ;
142- row [ `week${ w } ` ] = `❌${ reasonText } ` ;
143- }
144- } ) ;
145-
146- row . total = `${ s . totalPRs } PR / ${ s . totalReviews } Rev / ${ attendanceCount } 출석` ;
147- return row ;
169+ return {
170+ name : s . name ,
171+ totalPRs : `${ s . totalPRs } ` ,
172+ totalReviews : `${ s . totalReviews } ` ,
173+ } ;
148174 } ,
149175 } ;
150176
151- const markdownTable = createMarkdownTable (
152- MEMBERS . map ( ( m ) => m . githubId ) ,
153- tableConfig ,
154- ) ;
177+ const summaryTable = createMarkdownTable ( memberIds , summaryTableConfig ) ;
178+
179+ // ─── 리포트 생성 ───
155180 const reportTitle = `\`Session${ sessionData . id } \` 세션 활동 리포트` ;
156- const reportBody = `## THIS SESSION REPORT\n\n${ markdownTable } \n\n집계 시각: ${ getKSTDateString ( new Date ( ) ) } (KST)\n\n수고하셨습니다!` ;
181+ const reportBody = [
182+ `## THIS SESSION REPORT` ,
183+ `` ,
184+ `**${ sessionData . date . start } ~ ${ sessionData . date . end } **` ,
185+ `` ,
186+ ...activityTables . flatMap ( ( table ) => [ table , `` ] ) ,
187+ `### 총합` ,
188+ `` ,
189+ summaryTable ,
190+ `` ,
191+ `> 집계 시각: ${ getKSTDateString ( new Date ( ) ) } 22:00 (KST)` ,
192+ `` ,
193+ `수고하셨습니다!` ,
194+ ] . join ( "\n" ) ;
195+
196+ // ─── GitHub Discussion 생성 ───
197+ const repository = await getRepositoryInfo ( { github, context } ) ;
198+ const categories = await getDiscussionCategories ( { github, context } ) ;
199+ const categoryReport = categories . find ( ( cat ) =>
200+ cat . name . toLowerCase ( ) . includes ( "report" ) ,
201+ ) ;
157202
158203 if ( categoryReport ) {
159204 const thisSessionReport = await createDiscussion ( {
160205 github,
161- repoId : repoId ,
206+ repoId : repository . id ,
162207 categoryId : categoryReport . id ,
163208 title : reportTitle ,
164209 body : reportBody ,
@@ -174,8 +219,10 @@ export default async ({ github, context, core }) => {
174219 console . log ( `Session 리포트 생성 완료: ${ thisSessionReport . id } ` ) ;
175220
176221 return {
177- title : reportTitle ,
178- url : `https://github.com/${ context . repo . owner } /${ context . repo . repo } /discussions/${ thisSessionReport . number } ` ,
222+ reportData : {
223+ title : reportTitle ,
224+ url : `https://github.com/${ context . repo . owner } /${ context . repo . repo } /discussions/${ thisSessionReport . number } ` ,
225+ } ,
179226 } ;
180227 }
181228
0 commit comments