Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/workflows/update_challenge_progress.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Update Challenge Progress

on:
pull_request:
types: [opened, edited, reopened]

jobs:
update_progress:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
with:
ref: ${{ github.head_ref }}
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'

- name: Run extract_pr_data.py
working-directory: _MonthlyChallenges
run: python extract_pr_data.py

- name: Run update_scoreboard.py
working-directory: _MonthlyChallenges
run: python update_scoreboard.py

- name: Run update_dashboard.py
working-directory: _MonthlyChallenges
run: python update_dashboard.py

- name: Commit updated files
working-directory: _MonthlyChallenges
run: |
git config --global user.name "${{ secrets.GIT_USER_NAME }}"
git config --global user.email "${{ secrets.GIT_USER_EMAIL }}"
git add scoreboard.json DASHBOARD.md HISTORY.md
git commit -m "Update challenge progress dashboard" || echo "No changes to commit"
git push origin ${{ github.head_ref }}

- name: Post PR Comment with progress
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
// DASHBOARD.md 파일에서 내용 읽어오기 (working-directory에 따라 경로 조정)
const dashboard = fs.readFileSync('_MonthlyChallenges/DASHBOARD.md', 'utf8');
// PR 이벤트에서 PR 번호 가져오기
const prNumber = context.payload.pull_request.number;
// GitHub REST API를 통해 코멘트 생성
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: dashboard
});
Empty file added _MonthlyChallenges/DASHBOARD.md
Empty file.
Empty file added _MonthlyChallenges/HISTORY.md
Empty file.
68 changes: 68 additions & 0 deletions _MonthlyChallenges/extract_pr_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import os
import json
import re


def main():
# 1. GitHub 이벤트 페이로드 로드
event_path = os.getenv('GITHUB_EVENT_PATH')
print("event_path", event_path)
if not event_path:
print("GITHUB_EVENT_PATH 환경변수가 설정되어 있지 않습니다.")
exit(1)

with open(event_path, 'r', encoding='utf-8') as f:
event_data = json.load(f)
print(f"event_data: {event_data}")

pr_body = event_data['pull_request']['body']
pr_number = event_data['pull_request']['number']
username = event_data['pull_request']['user']['login']
print(f"pr_body: {pr_body}, pr_number: {pr_number}")

if pr_number is None:
print("PR 번호를 찾을 수 없습니다.")
exit(1)

# 2. PR 본문을 줄 단위로 분할
lines = pr_body.splitlines()
print(f"lines: {lines}")

# 3. "푼 문제" 섹션 탐지 및 문제 항목 파싱
in_problem_section = False
problem_entries = []

# 정규표현식 패턴:
# - [백준 #2169. 로봇 조종하기](https://www.acmicpc.net/problem/2169): DP / 골드2
pattern = r'- \[(?P<source>[^\]]+?) #(?P<id>\d+)\.\s+(?P<title>.*?)\]\((?P<link>.*?)\):\s*(?P<algorithm>[^/]+)\s*/\s*(?P<difficulty>.+)'

for line in lines:
# "푼 문제" 키워드를 찾으면 해당 섹션 시작으로 설정
if "푼 문제" in line:
in_problem_section = True
continue

if in_problem_section:
# bullet list 항목만 처리
if line.startswith('- '):
match = re.match(pattern, line)
print(f"match: {match}")
if match:
entry = match.groupdict()
print(f"entry: {entry}")
# 문제 번호를 정수로 변환
entry['problem_id'] = int(entry.pop("id"))
entry['pr_number'] = pr_number
entry['username'] = username
# 공백 제거
entry['algorithm'] = entry['algorithm'].strip()
entry['difficulty'] = entry['difficulty'].strip()
problem_entries.append(entry)
print(f"problem_entries: {problem_entries}")

# 4. 추출된 데이터를 pr_data.json 파일에 저장
with open("pr_data.json","w", encoding='utf-8') as f:
json.dump(problem_entries, f, ensure_ascii=False, indent=2)

if __name__ == '__main__':
main()
100 changes: 100 additions & 0 deletions _MonthlyChallenges/update_dashboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import os
import json
from datetime import datetime

SCOREBOARD_FILE = "scoreboard.json"
DASHBOARD_FILE = "DASHBOARD.md"
HISTORY_FILE = "HISTORY.md"

CHALLENGE_TYPES = {
"그래프": 5,
"DP": 5
}

def generate_dashboard(scoreboard):
# scoreboard 구조 예시 (새로운 형식):
# {
# "month": "2023-04",
# "users": {
# "user1": {
# "그래프": [101, 102],
# "DP": [2169, 1520],
# "achieved": {
# "그래프": true,
# "DP": false
# }
# },
# "user2": { ... }
# }
# }
month = scoreboard.get("month", "Unknown")
users = scoreboard.get("users", {})

md = f"# {month} 챌린지 진행 상황\n\n"
md += "| 사용자 | 챌린지 유형 | 문제 수 | 달성 여부 |\n"
md += "| ------ | ----------- | ------- | --------- |\n"

# 각 사용자별 진행 상황 표 작성
for user, data in users.items():
for ctype in CHALLENGE_TYPES.keys():
count = len(data.get(ctype, []))
achieved = data.get("achieved", {}).get(ctype, False)
achieved_str = "✅" if achieved else "❌"
md += f"| {user} | {ctype} | {count} | {achieved_str} |\n"

return md

def archive_current_month(scoreboard):
# HISTORY_FILE에 현재 달 기록을 추가 (append 방식)
dashboard_md = generate_dashboard(scoreboard)
with open(HISTORY_FILE, "a", encoding="utf-8") as f:
f.write(dashboard_md)
f.write("\n\n") # 구분을 위한 빈 줄 추가
print("HISTORY.md 업데이트 완료!")

def update_dashboard():
# 1. scoreboard.json 로드
if not os.path.exists(SCOREBOARD_FILE):
print(f"{SCOREBOARD_FILE} 파일이 없습니다.")
return

with open(SCOREBOARD_FILE, "r", encoding="utf-8") as f:
scoreboard = json.load(f)

# 2. 기존 파일 구조가 새 형식("month", "users")이 아니라면 변환
if "month" not in scoreboard or "users" not in scoreboard:
# 기존 구조는 사용자 이름이 최상위 키인 형태
scoreboard = {
"month": datetime.now().strftime("%Y-%m"),
"users": scoreboard
}
# 새 형식으로 저장
with open(SCOREBOARD_FILE, "w", encoding="utf-8") as f:
json.dump(scoreboard, f, ensure_ascii=False, indent=2)
print("기존 scoreboard 형식을 새 구조로 변환하였습니다.")

# 3. 현재 달 확인 및 월 초기화 처리
current_month = datetime.now().strftime("%Y-%m")
stored_month = scoreboard.get("month", current_month)
print(f"현재 달: {current_month}, 저장된 달: {stored_month}")

if stored_month != current_month:
print(f"새로운 달({current_month})로 넘어감 - 이전 달({stored_month}) 기록을 히스토리에 저장합니다.")
archive_current_month(scoreboard)
# scoreboard 초기화: users는 빈 dict, month는 현재 달로 갱신
scoreboard = {
"month": current_month,
"users": {}
}
with open(SCOREBOARD_FILE, "w", encoding="utf-8") as f:
json.dump(scoreboard, f, ensure_ascii=False, indent=2)

# 4. DASHBOARD.md 파일 생성 및 업데이트
md_content = generate_dashboard(scoreboard)
with open(DASHBOARD_FILE, "w", encoding="utf-8") as f:
f.write(md_content)

print("DASHBOARD.md 업데이트 완료!")

if __name__ == '__main__':
update_dashboard()
94 changes: 94 additions & 0 deletions _MonthlyChallenges/update_scoreboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import os
import json
from datetime import datetime

SCOREBOARD_FILE = "scoreboard.json"
PR_DATA_FILE = "pr_data.json"

# 챌린지 설정 (매달 투표 결과로 정해진 유형과 목표 문제 수)
CHALLENGE_TYPES = {
"그래프": 5,
"DP": 5
}


def initialize_user():
# 사용자별 초기 스코어보드 구조
return {
**{ctype: [] for ctype in CHALLENGE_TYPES.keys()},
"achieved": {ctype: False for ctype in CHALLENGE_TYPES.keys()}
}


def main():
# 1. 기존 스코어보드 로드 (없으면 빈 dict로 초기화)
if os.path.exists(SCOREBOARD_FILE):
with open(SCOREBOARD_FILE, 'r', encoding='utf-8') as f:
try:
scoreboard = json.load(f)
except json.JSONDecodeError:
scoreboard = {}
else:
scoreboard = {}

print(f"scorebard: {scoreboard}")

# 2. 새 구조("month", "users")가 없다면 변환
if "users" not in scoreboard or "month" not in scoreboard:
scoreboard = {
"month": datetime.now().strftime("%Y-%m"),
"users": scoreboard # 기존 scoreboard의 내용(사용자 데이터)이 있다면 여기에 넣음
}

users = scoreboard["users"]

# 3. pr_data.json 파일 로드
if not os.path.exists(PR_DATA_FILE):
print(f"{PR_DATA_FILE} 파일이 존재하지 않습니다.")
exit(1)

with open(PR_DATA_FILE, 'r', encoding='utf-8') as f:
pr_data = json.load(f)

print(f"pr_data: {pr_data}")

# 4. pr_data의 각 항목을 순회하며 사용자별 스코어보드 업데이트
for entry in pr_data:
username = entry["username"]
algorithm = entry["algorithm"]
problem_id = entry["problem_id"]

if not username or not algorithm or problem_id is None:
continue

print(f"username: {username}, algorithm: {algorithm}, problem_id: {problem_id}")

# 챌린지 유형에 포함되어 있는지 확인 (예: "그래프", "DP")
if algorithm not in CHALLENGE_TYPES:
continue # 챌린지 대상이 아니면 무시

# 사용자의 기록이 없으면 초기화
if username not in users:
scoreboard[username] = initialize_user()

# 해당 유형 문제 번호를 중복 없이 추가
if problem_id not in users[username].get(algorithm, []):
users[username][algorithm].append(problem_id)

print(f"users: {users}")

# 5. 각 사용자별로 달성 여부 업데이트
for username, data in users.items():
for ctype, goal in CHALLENGE_TYPES.items():
count = len(data.get(ctype, []))
# 목표 수 이상이면 달성 처리
data["achieved"][ctype] = (count >= goal)

# 5. 스코어보드 저장
with open(SCOREBOARD_FILE, 'w', encoding='utf-8') as f:
json.dump(scoreboard, f, ensure_ascii=False, indent=2)

print("scoreboard.json 업데이트 완료!")

if __name__ == '__main__':
main()
Loading