From 546bd54700a319bf15592c9ac271601be65c61fa Mon Sep 17 00:00:00 2001 From: th-kim0823 Date: Mon, 27 Apr 2026 19:51:35 +0900 Subject: [PATCH] feat: DEFAULT_TOPICS_SEED + ensure_topics_seeded Co-Authored-By: Claude Sonnet 4.6 --- assign_teams.py | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/e2e.py | 22 +++++++++++++ 2 files changed, 107 insertions(+) diff --git a/assign_teams.py b/assign_teams.py index cba18c9..6b2359c 100644 --- a/assign_teams.py +++ b/assign_teams.py @@ -12,6 +12,90 @@ import random from collections import Counter, defaultdict from pathlib import Path +DEFAULT_TOPICS_SEED = [ + { + "id": "T1", + "title": "내 인생 시간 도둑 처단기", + "tagline": "매일 짜증나는 반복 작업 하나를 2시간 안에 박살내기.", + "tone": "실용 + 살짝 치트키", + "items": [ + "Slack 멘션 자동 분류/요약기 — '진짜 날 부른 거' vs 'FYI' 분리", + "Jira 티켓 1줄 자동 요약 + 다음 액션 제안기", + "회의 캘린더 → 하루 시작 브리핑 (\"오늘 3개 있고 2개는 안 가도 됨\")", + "PR 리뷰 우선순위 큐 (크기/긴급도/차단여부 기반)", + "반복 쿼리/kubectl 명령 매크로 CLI — 자주 치는 10개를 1글자로", + "온콜 노이즈 필터 — 진짜 볼 알람 vs 무시해도 되는 알람", + "\"이번 주 내 활동 자동 요약\" — PR/티켓/리뷰 통합 리포트", + "Grafana 자주 보는 패널 즐겨찾기 통합 뷰", + "Slack 스레드 장문 요약기 — 놓친 채널 따라잡기용", + "\"이 회의 들어가야 함?\" 분류기 — 캘린더 제목·참석자 기반 추천", + ], + }, + { + "id": "T2", + "title": "벼르던 사이드 프로젝트", + "tagline": "평소 \"저거 하나 만들고 싶은데\" 하던 개인 토이를 2시간 안에 작은 완성품으로.", + "tone": "몰입 + 작은 완결성", + "items": [ + "내 PR/커밋 패턴 분석 개인 대시보드 (시간대/요일/사이즈 분포)", + "팀 Wiki/Notion을 터미널에서 fzf 스타일로 검색하는 CLI", + "사내 모델/데이터셋 메타데이터 검색 프로토타입", + "git 히스토리 인터랙티브 시각화 뷰어", + "\"오늘 내가 한 일\" 자동 일기 생성기 (커밋/PR/티켓 통합)", + "PR 코멘트 감정/톤 분석으로 팀 리뷰 문화 리포트", + "로컬 Kubernetes 리소스 관계 그래프 실시간 시각화", + "사내 논문/테크 문서 RAG 검색 도구", + "터미널에서 차트 포함된 마크다운 뷰어", + "북마크/링크 자동 분류·태깅 개인 도구", + ], + }, + { + "id": "T3", + "title": "오버엔지니어링 선수권", + "tagline": "평소 안 써본 무거운 기술 패턴을 일부러 작은 문제에 적용해 배우기.", + "tone": "학습형 오버엔지니어링", + "items": [ + "Todo 앱에 이벤트 소싱 + CQRS 제대로 적용", + "간단한 계산기 서비스에 OpenTelemetry 풀 트레이싱 구축", + "문서 검색 기능에 벡터 DB + 하이브리드 검색 (BM25 + 임베딩)", + "파일 업로드에 S3 presigned URL + 체크섬 검증 + 재시도 로직 정식 설계", + "팀 투표 기능을 Raft 합의 알고리즘으로 구현", + "회의실 예약을 Kafka 이벤트 스트리밍 기반으로", + "로컬 개발 환경을 완전한 K8s 매니페스트 (Deployment/Service/Ingress/HPA)로 재현", + "LLM + RAG 기반 PR 자동 리뷰 봇 아키텍처 설계·구현", + "멀티 에이전트 협업(프롬프트 2~3단계)으로 간단한 의사결정 시스템", + "사이드카 패턴으로 로깅/메트릭/인증 분리 데모", + ], + }, + { + "id": "T4", + "title": "팀에게 주는 작은 선물", + "tagline": "동료를 돕는 도구/봇/사이트. 특정인을 놀리는 게 아니라 팀 전체를 위한 것.", + "tone": "실질적 도움 + 가벼운 온기", + "items": [ + "배포 상태 집계·알림 봇 (성공/실패/롤백 요약)", + "신입 한 주 서바이벌 가이드 자동 생성기 (온보딩 링크/문서 수집)", + "팀 내부 용어/약어 사전 봇 — 신입/리서처 친화", + "아침 브리핑 봇 — 오늘 회의/배포/만료 알람 한방", + "점심 투표 1분 컷 봇 — 선택지 자동 생성 후 이모지 투표", + "팀 반복 질문 FAQ 봇 — 같은 질문 반복되는 채널용", + "온콜 교대 시 인수인계 자동 요약 생성기", + "회의실 스마트 추천 — 인원/시간대/위치 기반", + "사내 서비스 변경사항 요약 구독 봇", + "\"이번 주 팀 지표 한 장\" 리포트 — 머지 PR, 해결 티켓, 배포 수", + ], + }, +] + + +def ensure_topics_seeded(data): + """topics 비어있으면 default 시드. 기존 있으면 보존.""" + cats = data.get("topics", {}).get("categories", []) + if not cats: + data.setdefault("topics", {}) + data["topics"]["categories"] = DEFAULT_TOPICS_SEED + + # 시니어 명단 (Platform/Data/HPC/System만 알고 있음) SENIORS = { "한지승", "손현준", "강승형", "변수민", # Platform/Data @@ -271,6 +355,7 @@ def main(): "votes": [], } data["people"] = people_records + ensure_topics_seeded(data) # 신규 # 누락 키 보강 for k, v in [ ("settings", {"voting_open": True}), diff --git a/tests/e2e.py b/tests/e2e.py index d77b59b..da7af96 100644 --- a/tests/e2e.py +++ b/tests/e2e.py @@ -278,6 +278,27 @@ def t_topics_helpers(): assert get_topics() == sample2 +def t_topics_seeded_after_assign(): + from app import get_topics, _empty_state, save_data, load_data + # 빈 상태로 reset + save_data(_empty_state()) + assert get_topics() == [] + + from assign_teams import ensure_topics_seeded + data = load_data() + ensure_topics_seeded(data) + save_data(data) + + cats = get_topics() + assert len(cats) == 4 + for c in cats: + assert len(c["items"]) == 10 + assert c["id"] in ("T1", "T2", "T3", "T4") + assert c["title"] + assert c["tagline"] + assert c["tone"] + + if __name__ == "__main__": print(f"# E2E (data={TEST_DATA})\n") test("hackathon.json 로드 (34명, 7팀)", t_load) @@ -296,6 +317,7 @@ if __name__ == "__main__": test("load_data nested 키 backfill", t_load_data_backfills_nested_settings) test("stage 헬퍼", t_stage_helpers) test("topics 헬퍼", t_topics_helpers) + test("topics 시드", t_topics_seeded_after_assign) fails = sum(1 for r, _ in results if r == FAIL) print(f"\n# {len(results)} 중 통과 {len(results) - fails}, 실패 {fails}")