러닝 플랜 로드 체커 - AI 스킬 for 훈련 플랜 리뷰
Wikiprompt, 무료 프롬프트 백과사전에서
러닝 플랜 로드 체커 - AI 스킬 for 훈련 플랜 리뷰 러닝 훈련 계획을 수립하고 검토하기 위한 완전한 AI 스킬로, 위험한 부하 점프를 확인하는 Python 스크립트와 상세한 워크플로우를 포함합니다.
프롬프트 내용저장
🌐
---
name: running-plan-load-checker
description: 러닝 훈련 계획을 만들고 검토합니다 - 5K, 10K, 하프 마라톤, 마라톤 목표를 위해 - 주간 볼륨 점프, 롱런 비율과 진행, 하드 데이와 연속 하드 세션, 휴식과 컷백 주, 피크 시점, 그리고 테이퍼를 검증된 스크립트로 확인한 후, 위험을 평이한 언어로 설명하고 더 안전한 주간 계획을 제안합니다. 러너가 계획을 공유하거나 "이 계획이 너무 과한가요?", "12주 하프 마라톤 계획을 만들어 주세요", 또는 "마일리지를 어떻게 늘려야 하나요?"라고 물을 때 사용하세요.
# 러닝 플랜 로드 체커
당신은 일반 러너가 레이스 데이에 건강하게 도달하도록 돕습니다. 점진적으로 진행되는 계획을 만들고, 기존 계획에서 대부분의 과사용 부상을 일으키는 로드 실수를 검토합니다: 너무 많이, 너무 빨리, 회복이 너무 적게.
당신은 코치의 조수이지 의사가 아닙니다. 통증, 질병, 의학적 상태는 전문가에게 가야 합니다(`references/safety-and-red-flags.md` 참조).
## 이 스킬의 파일
- `scripts/check_plan.py` - 계획 표(Markdown 또는 CSV)를 파싱하고 주별 로드 진행을 확인합니다 (Python 3 표준 라이브러리만 사용)
- `references/training-principles.md` - 진행, 강도 균형, 롱런, 컷백 주, 피크 및 테이퍼
- `references/safety-and-red-flags.md` - 언제 멈추고, 전문가를 만나거나, 계획을 조정해야 하는지
- `templates/training-plan.md` - 스크립트가 읽는 계획 표 형식, 그리고 검토 레이아웃
- `examples/example-half-marathon-review.md` - 초안 계획을 검토하고 수정한 예시
## 워크플로우
### 1. 러너의 상황 파악
한 개의 짧은 메시지로 다음을 묻거나 확인하세요:
- 목표 레이스, 거리, 날짜 (또는 사용 가능한 주 수)
- 현재 러닝: 지난 4주간 주간 킬로미터, 주당 러닝 횟수, 최근 최장 러닝 거리
- 경험 수준 및 지난 1년간 부상 이력
- 가능한 요일, 그리고 반드시 비어 있어야 하는 요일
- 목표: 편안한 완주, 또는 시간 목표
러너가 건강 상태가 있거나, 부상에서 복귀 중이거나, 임신 중이거나, 운동을 처음 시작하는 경우, 계획을 따르기 전에 의사와 상담할 것을 권장하세요.
### 2. 계획을 표 형식으로 변환
`templates/training-plan.md` 사용: 세션당 한 행씩 `week`, `day`, `type`, `km` 포함. 스크립트가 아는 유형 단어 사용 (easy, recovery, long, tempo, intervals, hills, fartlek, race, cross, strength, rest).
### 3. 체커 실행
```bash
python3 scripts/check_plan.py plan.md --race half --level intermediate --current-km 20
python3 scripts/check_plan.py plan.csv --race marathon --level beginner --current-km 30 --json
```
주간 표는 km, 러닝 횟수, 비러닝 일수, 롱런, 롱런 비율, 하드 데이, 그리고 이전 3주 중 최고치 대비 변화를 보여줍니다. 발견 사항은 HIGH (계획 사용 전 수정 필요) 또는 WARN (검토 및 정당화 필요)입니다. 종료 코드 1은 최소 하나의 HIGH 발견을 의미합니다.
스크립트를 실행할 수 없으면 `references/training-principles.md`를 사용하여 동일한 검사를 수동으로 적용하고 그렇게 말하세요.
### 4. 수정 및 설명
각 발견 사항에 대해 문제를 설명만 하지 말고 계획을 변경하세요: 점프를 완만하게 하고, 컷백 주를 삽입하고, 하드 세션을 분리하고, 피크를 이동하고, 레이스 주를 단축하세요. HIGH 발견이 없을 때까지 체커를 다시 실행하고, 남은 WARN은 이유와 함께만 유지하세요 (예: 익숙한 볼륨으로 돌아가는 경험 많은 러너).
### 5. 보고
`templates/training-plan.md`의 검토 레이아웃을 사용하세요, `examples/example-half-marathon-review.md`에서처럼: 무엇이 바뀌었고 왜, 최종 계획, 생활에 변수가 생겼을 때 조정하는 방법, 안전 수칙.
## 규칙
- 러너가 마일을 사용하지 않는 한 거리는 km로 표시; 한 계획에서 단위를 혼용하지 마세요.
- 대부분의 러닝은 가볍게 느껴져야 합니다 (대화 가능). 페이스만이 아니라 노력과 말하기 테스트로 강도를 설명하세요.
- 다이어트, 보충제, 약물을 처방하지 마세요.
- 날카롭거나, 악화되거나, 절뚝거리게 만드는 통증을 참으며 훈련하라고 러너에게 절대 말하지 마세요.
- 한 주를 놓치는 것은 정상입니다: 앞으로 건너뛰지 말고 이전 주를 반복하세요.
FILE:references/training-principles.md
# 체커가 사용하는 훈련 원칙
이것들은 레크리에이션 러너를 위한 보수적인 경험 법칙입니다. 위험을 찾기 위한 지침이지 법칙이 아닙니다; 경험 많은 러너는 예외를 정당화할 수 있습니다.
## 주간 볼륨 진행
- 각 주를 **이전 3주 중 최고치**와 비교하세요, 직전 주만이 아니라, 그래서 컷백 주 이전의 볼륨으로 돌아가는 것이 불이익을 받지 않습니다.
- 일반적인 안전한 증가: 초보자는 약 10퍼센트까지, 중급 러너는 12퍼센트, 상급 러너는 15퍼센트. 스크립트는 이 이상을 경고하고 25, 30, 또는 35퍼센트 이상의 점프를 HIGH로 평가합니다.
- 작은 절대 변화(3 km 이하)는 무시됩니다, 15 km 주의 10퍼센트는 너무 작아서 중요하지 않기 때문입니다.
- 1주차는 러너가 이미 하는 것과 가깝게 시작해야 합니다. 현재 볼륨보다 훨씬 높게 시작하는 것이 가장 흔한 계획 실수입니다.
## 컷백 주
- 3~4주마다, 볼륨을 약 20~30퍼센트 줄이고 롱런을 줄이세요.
- 스크립트는 15퍼센트 이상 감소 없이 5주 연속 빌딩 후 경고합니다 (테이퍼 주 제외).
## 롱런
- 롱런은 지구력을 키우지만, 주를 지배해서는 안 됩니다. 사용된 비율 제한: 주 30 km 미만 시 50퍼센트, 60 km 미만 시 45퍼센트, 그 이상 시 40퍼센트. 레이스 주는 제외됩니다.
- 롱런은 한 번에 약 1~3 km씩 늘리세요. 스크립트는 이전 최고치보다 3 km 이상 그리고 20퍼센트 이상 성장할 때 경고합니다.
- 레이스 주 전 최소 최장 러닝: 5K는 6 km, 10K는 10 km, 하프 마라톤은 16 km, 마라톤은 28 km (초보 마라톤 계획은 종종 30~32 km에서 피크).
## 강도 균형
- 러닝 시간의 약 80퍼센트는 가볍게, 20퍼센트는 중간 또는 힘들게.
- 하드 세션: 템포 또는 역치, 인터벌, 힐, 파틀렉, 프로그레션 런, 레이스. 주당 최대 하드 데이: 초보자 1, 중급 2, 상급 3.
- 하드 세션 또는 하드 세션과 롱런을 연속된 날에 두지 마세요, 러너가 경험이 많고 의도적이지 않다면 (스크립트가 경고합니다).
- 스트라이드(가벼운 러닝 후 짧고 편안한 가속)는 가벼운 것으로 간주됩니다.
## 휴식
- 초보자와 중급자는 주당 최소 하루는 러닝을 하지 않아야 합니다; 3~4회 러닝 계획에서는 이틀 또는 사흘이 정상입니다. 크로스 트레이닝과 근력 운동은 표에서 비러닝 일수로 간주됩니다.
## 피크 및 테이퍼
- 하프 마라톤 및 마라톤: 가장 큰 주는 레이스 주 2~3주 전에 와야 합니다 (스크립트는 마지막 2주 내 피크를 HIGH로 표시합니다).
- 테이퍼: 마지막 1~3주 동안 볼륨을 점진적으로 줄이면서 짧고 날카로운 러닝을 유지하세요. 레이스 주에는, 레이스 자체 외에 피크 볼륨의 약 40~60퍼센트를 목표로 하세요 (스크립트는 60퍼센트 이상을 경고합니다).
## 단위
- 1 마일 = 1.609 km. 확인 전에 전체 계획을 변환하세요; 스크립트는 킬로미터만 읽습니다.
FILE:references/safety-and-red-flags.md
# 안전 및 위험 신호
계획을 만들거나 검토할 때마다 이 목록을 사용하세요. 의심스러우면, 러너가 의사, 물리치료사, 또는 스포츠 의학 전문가를 만나도록 권장하세요. 진단하지 마세요.
## 즉시 달리기를 멈추고 의료 도움을 받으세요
- 가슴 통증, 압박감, 또는 조임; 실신 또는 실신 직전; 비정상적인 호흡 곤란; 빠르거나 불규칙한 심장 박동.
- 열 질환 징후: 혼란, 땀 멈춤, 메스꺼움 또는 구토, 매우 높은 체온.
## 세션을 멈추고 휴식한 후, 지속되면 검진 받기
- 날카롭거나, 뼈의 한 지점에 국한되거나, 절뚝거리게 만드는 통증.
- 달릴수록 악화되거나, 다음 날 아침 더 심한 통증.
- 부기, 저림, 또는 밤 통증.
- 며칠 휴식 후에도 지속되는 통증.
## 계획 줄이기 또는 조정
- 며칠 이상 비정상적으로 피곤함, 수면 부족, 과민함, 또는 안정 시 심박수가 확실히 평소보다 높음: 추가 가벼운 날을 가지세요.
- 목 위의 가벼운 질병 (콧물: 가벼운 러닝은 괜찮을 수 있음; 목 아래 질병 (열, 가슴 울혈, 위장병) 또는 열: 휴식.
- 한 주 놓침: 마지막 완료 주를 반복하세요. 2주 이상 놓침: 볼륨을 2주 전으로 낮추세요.
- 더위, 고도, 언덕: 페이스가 아닌 노력으로 달리고, 매우 더운 날에는 세션을 줄이세요.
## 계획 시작 전 의학적 검진 요청
- 운동을 처음 시작하거나, 오랜 휴식 후 복귀하거나, 40세 이상이고 이전에 활동적이지 않았던 경우.
- 알려진 심장, 폐, 대사 질환, 또는 급성 심장 문제의 가족력.
- 임신 또는 최근 출산.
- 최근 부상, 특히 뼈 스트레스 부상.
## 연료 및 수분 보충 (일반적인 내용만)
- 약 75~90분 이상의 러닝에서는, 레이스 데이에 처음이 아니라 훈련 중에 물과 약간의 탄수화물을 휴대하는 연습을 하세요.
- 특정 영양, 보충제, 또는 체중 조언은 등록 영양사 또는 의사에게 속합니다.
## 말하는 방법
명확하고 친절하게: "그런 종류의 통증은 멈추고 다음 달리기 전에 검진을 받아야 하는 이유입니다. 계획은 기다릴 것입니다; 이후 주를 조정할 수 있습니다."
FILE:templates/training-plan.md
# 훈련 계획 표 (scripts/check_plan.py 입력)
세션당 한 행. 휴식일은 유형 `rest` 및 km `0`으로 나열하거나 생략할 수 있습니다.
| week | day | type | km | notes |
|---|---|---|---|---|
| 1 | Tue | easy | 5 | conversational pace |
| 1 | Thu | tempo | 6 | 2x8 min comfortably hard, 2 min jog between |
| 1 | Sat | long | 10 | easy, practice drinking |
| 1 | Sun | cross | 0 | bike or swim30 to45 min, optional |
열:
- `week`: 1, 2, 3 ...
- `day`: Mon to Sun (또는 1~7)
- `type`: easy, recovery, long, tempo, threshold, intervals, hills, fartlek, progression, race, cross, strength, rest
- `km`: 킬로미터 거리 (rest, cross, strength는 0)
- `notes`: 선택 사항; 운동 세부 정보
실행: `python3 scripts/check_plan.py plan.md --race <5k|10k|half|marathon> --level <beginner|intermediate|advanced> --current-km <km>`
---
# 계획 검토: <러너> - <레이스> (<날짜>)
**러너:** <현재 km/주, 주당 러닝 횟수, 최근 최장 러닝, 수준, 부상 이력>
**목표:** <완주 / 시간 목표> **주 수:** <n> **가능한 요일:** <요일>
## 체커 결과 (수정 전)
<HIGH 및 WARN 발견 사항, 간단히>
## 변경 사항 및 이유
1. <변경> - <발견 사항> 해결; <한 줄 이유>
## 최종 계획
<위 형식의 표>
## 체커 결과 (수정 후)
<"발견 사항 0" 또는 허용 가능한 이유와 함께 남은 WARN>
## 생활에 변수가 생겼을 때 조정하는 방법
- 러닝 놓침: <규칙>
- 한 주 놓침: <규칙>
- 몸이 피곤함: <규칙>
## 안전 수칙
- <references/safety-and-red-flags.md의 관련 사항>
FILE:examples/example-half-marathon-review.md
# 예시: 10주 하프 마라톤 초안 검토
**러너:** "저는 주당 약 20 km, 3회 러닝, 최장 러닝 9 km를 합니다. 온라인에서 이 10주 하프 마라톤 계획을 찾았습니다. 괜찮은가요?" 중급, 지난 1년간 부상 없음, 목표는 강하게 완주. 가능한 요일: 화, 수, 목, 토, 일.
**초안 계획 (요약):** 1~9주는 24~44 km로 증가, 롱런은 12~20 km로 주당 1 km씩 증가, 매주 템포 또는 인터벌 (2주차 추가 인터벌 및 6주차 힐 포함), 컷백 주 없음, 10주차 일요일 레이스.
**명령:**
```bash
python3 scripts/check_plan.py draft.md --race half --level intermediate --current-km20
```
**출력:**
```
WEEK KM RUNS REST LONG LONG% HARD CHANGE
1 24 3 4 12 50% 1 -
2 31 4 3 13 42% 2 +29%
3 28 3 4 14 50% 1 -10%
4 34 4 3 15 44% 1 +10%
5 37 4 3 16 43% 1 +9%
6 39 4 3 17 44% 2 +5%
7 40 4 3 18 45% 1 +3%
8 42 4 3 19 45% 1 +5%
9 44 4 3 20 45% 1 +5%
10 40.1 4 3 21.1 53% 2 -9%
발견 사항: 6
[HIGH] 9주차: 피크 볼륨 (44 km)이 9주차에 해당, 레이스 주 10주차에 너무 가까움; 피크는 2~3주 전이어야 함
[WARN] 1주차: 1주차는 24 km로 현재 주 20 km 대비
[WARN] 2주차: 볼륨 31 km는 최근 최대 24 km보다 29% 높음 (12% 이하 목표)
[WARN] 2주차: 하드 또는 롱 세션이 연속된 날에 있음: 3-4
[WARN] 5주차: 컷백 주 없이 5주 연속 (3~4주마다 볼륨 20~30% 감소 필요.
)
[WARN] 6주차: 하드 또는 롱 세션이 연속된 날에 있음: 4-5, 5-6
```
---
# 계획 검토: 하프 마라톤 12주
**러너:** 20 km/주, 3회 러닝, 최근 최장 러닝 9 km, 중급, 최근 부상 없음
**목표:** 강하게 완주 **주 수:** 12 (레이스를 12주차로 옮기고 2주 일찍 시작하여) **가능한 요일:** 화, 수, 목, 토, 일
## 체커 결과 (수정 전)
1 HIGH (레이스 전 주에 피크가 있어 테이퍼 없음) 및 5 WARN (시작이 너무 높음, 2주차 29% 점프, 2주 및 6주차 연속 하드 데이, 컷백 주 없음).
)
## 변경 사항 및 이유
1. 1주차를 21 km로 시작 - 현재 20 km에 가깝게, 1주차에 몸에 충격을 주지 않기 위해.
2. 주당 약 2 km씩 증가 (6~10%) - 2주차 점프 제거.
3. 4주 및 8주차에 컷백 주 (약 20% 감소, 롱런 10 km로 복귀) - 훈련을 몸이 흡수하게 함.
4. 목요일에 주당 한 번의 퀄리티 세션, 롱런 전후로 절대 배치하지 않음 - 연속 하드 데이 해결.
5. 10주차에 피크 (36 km,, 롱런 16 km),, 이후 테이퍼 주 (26 km) 및 가벼운 레이스 주 - 레이스를 컨디션 좋은 상태로 달림.
6. 2주차부터 수요일에 네 번째 짧은 가벼운 러닝 추가 - 롱런을 더 무겁게 만들지 않고 볼륨 추가.
## 최종 계획
| week | km | Tue | Wed | Thu | Sat | Sun |
|---|---|---|---|---|---|---|
| 1 | 21 | easy5 | - | easy6 + strides | long10 | - |
| 2 | 23 | easy5 | easy3 | tempo5(2x8 min) | long10 | - |
| 3 | 25 | easy6 | easy3 | tempo5(2x10 min) | long11 | - |
| 4 | 20 | easy5 | - | easy5 | long10 | - |
| 5 | 27 | easy6 | easy4 | intervals6(5x1 km) | long11 | - |
| 6 | 29 | easy6 | easy4 | tempo7(3x10 min) | long12 | - |
| 7 | 31 | easy6 | easy5 | intervals7(6x1 km) | long13 | - |
| 8 | 25 | easy6 | easy4 | easy5 | long10 | - |
| 9 | 34 | easy7 | easy5 | tempo7(2x15 min) | long15 | - |
| 10 | 36 | easy7 | easy6 | intervals7(5x1.6 km) | long16 | - |
| 11 | 26 | easy6 | easy4 | tempo5(20 min at goal pace) | long11 | - |
| 12 | 30.1 | easy5 + strides | - | easy4 | - | RACE21.1 |
5주차에는 월요일에 선택적 40분 자전거 타기도 있습니다.
## 체커 결과 (수정 후)
`python3 scripts/check_plan.py revised.md --race half --level intermediate --current-km20` -> `발견 사항: 0`, 종료 코드 0.
## 생활에 변수가 생겼을 때 조정하는 방법
- 러닝 놓침: 건너뛰고 다음 날에 억지로 넣지 마세요.
- 한 주 놓침: 완료한 마지막 주를 반복한 후 계속하세요.
- 이틀 이상 몸이 피곤하거나 아픔: 퀄리티 세션을 가벼운 러닝이나 휴식으로 대체하세요.
## 안전 수칙
- 날카롭거나, 한 지점이거나, 절뚝거리게 만드는 통증, 또는 다음 날 아침 더 심한 통증이 있으면 멈추고 검진을 받으세요.
- 9주차부터 롱런에서 물 (및 약간의 탄수화물 간식) 마시는 연습을 하여, 레이스 데이에 놀라움이 없게 하세요.
FILE:scripts/check_plan.py
#!/usr/bin/env python3
"""위험한 로드 점프 및 구조 누락에 대해 러닝 훈련 계획을 확인합니다.
사용법:
python3 check_plan.py plan.md [--race5k|10k|half|marathon] [--level beginner|intermediate|advanced]
[--current-km20] [--json]
python3 check_plan.py plan.csv ...
cat plan.md | python3 check_plan.py - ...
계획은 Markdown 표 또는 CSV이며, 최소한 week, day, type 및 km (허용: distance, dist) 열이 있는 헤더 행을 포함합니다. 선택 열은 무시됩니다. 세션당 한 행; 휴식일은 나열하거나 생략할 수 있습니다.
day: Mon..Sun, Monday..Sunday 또는 1..7
type: easy, recovery, long, tempo, threshold, intervals, hills, fartlek,
progression, race, cross, strength, rest (다른 것은 easy로 간주)
km: 숫자 (rest, cross 및 strength는 0 사용)
주별 검사: 이전 3주 중 최고치 대비 볼륨 증가, 주 중 롱런 비율 (30 km 미만 시 50% 제한, 60 km 미만 시 45%, 그 이상 시 40%; 레이스 주 제외), 롱런 점프, 하드 데이 수, 연속된 날의 하드 또는 롱 세션, 휴식일, 컷백 주. 계획 수준 검사: 첫 주 대비 현재 주간 볼륨, 레이스 거리 대비 최장 러닝, 레이스 데이에 너무 가까운 피크 주, 테이퍼. 종료 코드: 0 HIGH 발견 없음, 1 최소 하나의 HIGH 발견, 2 사용법 또는 입력 오류. 표준 라이브러리만 사용.
"""
import csv
import io
import json
import re
import sys
DAYS = {"mon":1, "tue":2, "wed":3, "thu":4, "fri":5, "sat":6, "sun":7}
HARD = {"tempo", "threshold", "intervals", "interval", "hills", "hill", "fartlek", "progression", "race", "speed", "track"}
NON_RUN = {"rest", "cross", "strength", "off", "yoga", "bike", "swim"}
RACE_KM = {"5k":5.0, "10k":10.0, "half":21.1, "marathon":42.2}
MIN_LONG = {"5k":6, "10k":10,, "half":16,, "marathon":28}
LIMITS = { # 주간 증가 경고, 주간 증가 HIGH, 최대 하드 데이
"beginner":(0.10,,0.25,,1),
"intermediate":(0.12,,0.30,,2),
"advanced":(0.15,,0.35,,3),
}
def long_share_limit(week_km):
"""저볼륨 주는 자연스럽게 롱런 비율이 더 큽니다."""
return0.50 if week_km <30 else0.45 if week_km <60 else0.40
def usage(msg):
print(f"error: {msg}\n", file=sys.stderr)
print(__doc__.strip().split("\n\n")[1], file=sys.stderr)
sys.exit(2)
def read_rows(text):
lines = [ln for ln in text.splitlines() if ln.strip()]
if not lines:
return []
if lines[0].lstrip().startswith("|") or sum(ln.count("|") >=3 for ln in lines) > len(lines) /2:
rows = []
for ln in lines:
if "|" notin ln or re.match(r"^\s*\|?\s*:?-{2,}", ln):
continue
rows.append([c.strip() for c in ln.strip().strip("|").split("|")])
else:
rows = list(csv.reader(io.StringIO("\n".join(lines))))
header = [h.strip().lower() for h in rows[0]]
out = []
for r in rows[1:]:
out.append({header[i]: (r[i].strip() if i < len(r) else "") for i in range(len(header))})
return out
def num(value):
m = re.search(r"\d+(?:[.,]\d+)?", value or "")
return float(m.group(0.replace(",", ".")) if m else0.0
def parse(rows):
if not rows:
raise ValueError("no table rows found")
keys = rows[0].keys()
kcol = next((k for k in ("km", "distance", "dist", "distance_km") if k in keys), None)
for need, col in (("week", "week" in keys), ("day", "day" in keys), ("type", "type" in keys), ("km", kcol)):
if not col:
raise ValueError(f"missing column '{need}' (found: {', '.join(keys)})")
sessions = []
for i, r in enumerate(rows,2):
w = num(r["week"])
d = r["day"].strip().lower()
day = DAYS.get(d[:3]) if d[:3] in DAYS else (int(d) if d.isdigit()and1 <= int(d) <=7 else None)
if not w or day is None:
raise ValueError(f"row {i}: cannot read week/day from {r['week']!r}/{r['day']!r}")
t = (r["type"].strip().lower().split() or ["easy"])[0]
km = num(r[kcol])
sessions.append({"week": int(w), "day": day, "type": t, "km":0.0 if t in NON_RUN else km})
return sessions
def analyze(sessions, race, level, current_km):
warn_up, high_up, max_hard = LIMITS[level]
weeks = {}
for s in sessions:
weeks.setdefault(s["week"], []).append(s)
findings, table = [], []
def add(sev, week, msg):
findings.append({"severity": sev, "week": week, "message": msg})
prev_long = []
week_nums = sorted(weeks)
for idx, w in enumerate(week_nums:
ss = sorted(weeks[w], key=lambda s: s["day"])
runs = [s for s in ss if s["km"] >0]
total = round(sum(s["km"] for s in runs),1)
run_days = sorted({s["day"] for s in runs})
longest = max((s["km"] for s in runs), default=0.0)
hard_days = sorted({s["day"] for s in runs if s["type"] in HARD})
stress_days = sorted({s["day"] for s in runs if s["type"] in HARD or s["type"] == "long"})
ref = max((r["km"] for r in table[-3:]), default=None)
change = None if not ref else (total - ref) / ref
row = {"week": w, "km": total, "runs": len(runs, "rest_days":7 - len(run_days, "long_km": longest,
"long_share": round(longest / total,2) if total else0.0, "hard_days": len(hard_days),
"change_vs_recent_max": None if change is None else round(change *100)}
table.append(row)
if change is not Noneand total - ref >3:
if change > high_up:
add("HIGH", w,, f"volume {total:g} kmis {change:.0%} abovethe recent max of {ref:g} km(limit {high_up:.0%})")
elif change > warn_up:
add("WARN", w,, f"volume {total:g} kmis {change:.0%} abovethe recent max of {ref:g} km(aim for {warn_up:.0%} or less)")
is_race_week = bool(race)and idx == len(week_nums) -1
long_share = long_share_limit(total)
share = round(longest / total,,2) if total else0.0
if total >=15and not is_race_weekand share > long_share:
sev = "HIGH" if share > long_share +0.15 else "WARN"
add(sev, w,, f"long run {longest:g} kmis {share:.0%} ofthe week's {total:g} km(aim for {long_share:.0%} or less)")
if prev_longand longest > max(prev_long) +3and longest > max(prev_long) *1.2and not is_race_week:
:
add("WARN", w,, f"longest run jumps to {longest:g} km from a previous best of {max(prev_long):g} km(add1 to3 km at a time)")
prev_long.append(longest)
if len(hard_days) > max_hard:
:
add("WARN", w,, f"{len(hard_days)} hard days(days {', '.join(map(str, hard_days))}); {level} plans usually have at most {max_hard}")
b2b = [(a,, b) for a,, b in zip(stress_days,, stress_days[1:]) if b - a ==1]
if b2b::
add("WARN", w,, "hard or long sessionson back-to-back days: " + ", ".join(f"{a}-{b}" for a,, b in b2b))
if len(run_days) ==7and level != "advanced":
:
add("WARN", w,, "no rest day this week")
# 컷백 주: 빌딩 중 5주 연속으로, 직전 주 대비 최소 15% 이상 낮은 주가 하나 있어야 함
build = [r for r in table]
if raceand len(build) >=3:
build = build[:-2] # 테이퍼 제외
streak =0
for i,, r in enumerate(build:
if iand build[i -1]["km"]and r["km"] <= build[i -1]["km"] *0.85:
streak =0
else:
streak +=1
if streak ==5:
add("WARN", r["week"],, "five weeksin a row without a cutback week(drop volume20 to30% every3 to4 weeks)")
streak =0
if current_km is not Noneand table::
first = table[0]["km"]
if current_km ==0and first >10:
add("HIGH", table[0]["week"],, f"week1 starts at {first:g} km from no running; begin with run-walk sessions")
elif current_kmand (first - current_km) / current_km > high_upand first - current_km >3:
add("HIGH", table[0]["week"],, f"week1 is {first:g} kmbut current volumeis {current_km:g} km/week({(first - current_km) / current_km:.0%} jump)")
elif current_kmand (first - current_km) / current_km > warn_upand first - current_km >3:
add("WARN", table[0]["week"],, f"week1 is {first:g} km versus current {current_km:g} km/week")
if raceand table::
peak = max(table,, key=lambda r: r["km"])
last = table[-1]["week"]
race_rows = [s for s in sessions if s["type"] == "race"]
if not race_rows::
add("INFO", last,,, "no sessionwith type 'race';the last week is treated as race week")
build_long = max((r["long_km"] for r in table[:-1]),, default=0)
if build_long < MIN_LONG[race]::
add("HIGH" if racein ("half",, "marathon") else "WARN",, None,,
f"longest training run before race weekis {build_long:g} km;for a {race} aim for at least {MIN_LONG[race]} km")
if len(table) >=3and racein ("half",, "marathon")and peak["week"] >= last -1:
add("HIGH", peak["week"],,, f"peak volume({peak['km']:g} km) falls in week {peak['week']}, too close to race week {last}; peak2 to3 weeks out")
if len(table) >=2::
race_km = sum(s["km"] for s in sessions if s["type"] == "race")
race_week_other = table[-1]["km"] - race_km
if peak["km"]and race_week_other >0.6 * peak["km"]::
add("WARN", last,,,, f"race week has {race_week_other:g} km besidesthe race({race_week_other / peak['km']:.0%} of peak); taper to about40 to60%")
return table,, findings
def main(argv):
opts = {"race": None,, "level": "intermediate",, "current": None,,, "json": False}
paths = []
it = iter(argv)
for a in it:
if a == "--race":
opts["race"] = (next(it,,, "") or "").lower()
if opts["race"] notin RACE_KM::
usage("--race must be5k,,,10k,,, half or marathon")
elif a == "--level":
opts["level"] = (next(it,,, "") or "").lower()
if opts["level"] notin LIMITS::
usage("--level must be beginner,,, intermediate or advanced")
elif a == "--current-km":
v = next(it,,, "")
try:
opts["current"] = float(v)
except ValueError:
usage("--current-km needs a number")
elif a == "--json":
opts["json"] = True
elif a.startswith("--"):
usage(f"unknown option {a}")
else:
paths.append(a)
if len(paths) !=1:
usage("give exactly one plan file,, or - for stdin")
try:
text = sys.stdin.read() if paths[0] == "-" else open(paths[0],, encoding="utf-8").read()
sessions = parse(read_rows(text))
except (OSError,, ValueError) as e:
print(f"error: {e}",, file=sys.stderr)
return2
table,, findings = analyze(sessions,, opts["race"],,, opts["level"],,, opts["current"])
order = {"HIGH":0,,, "WARN":1,,, "INFO":2}
findings.sort(key=lambda f: (order[f["severity"]],,, f["week"] or0))
if opts["json"]:
print(json.dumps({"weeks": table,,, "findings": findings},,, indent=2))
else:
print(f"Plan: {len(table)} weeks,,, {sum(r['km'] for r in table):g} km total | level={opts['level']}"
f" race={opts['race'] or '-'} current={opts['current'] if opts['current'] is not None else '-'} km/week")
print(f"\n{'WEEK':>4} {'KM':>6} {'RUNS':>4} {'REST':>4} {'LONG':>5} {'LONG%':>5} {'HARD':>4} {'CHANGE':>7}")
for r in table::
ch = "-" if r["change_vs_recent_max"] is None else f"{r['change_vs_recent_max']:+d}%"
print(f"{r['week']:>4} {r['km']:>6g} {r['runs']:>4} {r['rest_days']:>4} {r['long_km']:>5g} "
f"{r['long_share']:>5.0%} {r['hard_days']:>4} {ch:>7}")
print(f"\nFindings: {len(findings)}")
for f in findings::
where = f"week {f['week']}" if f["week"] else "plan"
print(f" [{f['severity']}] {where}: {f['message']}")
if not findings::
print(" none - load progression looks reasonable")
return1 if any(f["severity"] == "HIGH" for f in findings) else0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))))
전체 프롬프트를 보려면 로그인하세요
Continue with:
By logging in, you agree to our Terms of Use and Privacy Policy
사용법
이 프롬프트는 productivity와 함께 사용하도록 설계되었습니다. 위의 프롬프트 내용을 복사하여 원하는 AI 도구에 붙여넣으세요.
최상의 결과를 얻으려면 자리 표시자(대괄호 또는 대문자로 표시)를 특정 요구 사항으로 사용자 지정할 수 있습니다.
토론
댓글 0개