Skip to content

Commit aa40809

Browse files
committed
refactor: update session report table format
1 parent 2b7475f commit aa40809

File tree

5 files changed

+140
-752
lines changed

5 files changed

+140
-752
lines changed

.github/data/session/session_6.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
{
1818
"week": 50,
1919
"date": {
20-
"start": "2026-04-2",
20+
"start": "2026-04-02",
2121
"end": "2026-04-19"
2222
},
2323
"list": [

.github/scripts/rule-monitor-session.js

Lines changed: 120 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import sessionData from "../data/session/session_6.json" with { type: "json" };
21
import { MEMBERS, STUDY_CONFIG } from "./utils/constants.js";
32
import { getKSTDateString } from "./utils/date.js";
3+
import { getLatestSessionData } from "./utils/session.js";
44
import { createMarkdownTable } from "./utils/formatter.js";
55
import {
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

.github/workflows/rule-monitor-session.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ on:
44
schedule:
55
- cron: "0 13 * * 0"
66
workflow_dispatch:
7+
inputs:
8+
test:
9+
description: "날짜 체크 건너뛰기 (테스트용)"
10+
required: false
11+
type: boolean
12+
default: false
713

814
permissions:
915
contents: read
@@ -29,8 +35,9 @@ jobs:
2935
with:
3036
github-token: ${{ secrets.GITHUB_TOKEN }}
3137
script: |
38+
const test = '${{ inputs.test }}' === 'true';
3239
const { default: run } = await import(process.env.SCRIPT_FULL_PATH);
33-
return await run({ github, context, core });
40+
return await run({ github, context, core, test });
3441
3542
- name: send discord notification
3643
if: steps.monitor_step.outputs.discussion_title != 'null' && steps.monitor_step.outputs.result != ''

0 commit comments

Comments
 (0)