Updated commands/injuries/CLAUDE.md to reflect playoff week validation:
Documentation Changes:
- Updated /injury set-new parameters to show week range 1-21
- Added "Week and Game Validation" section with playoff-specific limits
- Updated automatic calculations note for variable games per week
- Added playoff examples (weeks 19-21 with proper game numbers)
- Added new "Configuration" section documenting playoff constants
- Added week/game limits table showing all season phases
- Updated Key Improvements to mention playoff support
- Updated Last Updated to October 2025
Validation Details Now Documented:
- Regular Season (Weeks 1-18): 1-4 games per week
- Playoff Round 1 (Week 19): 1-5 games (best of 5)
- Playoff Round 2 (Week 20): 1-7 games (best of 7)
- Playoff Round 3 (Week 21): 1-7 games (best of 7)
Configuration Section:
Documents the playoff constants from config.py and explains:
- How constants are used in views/modals.py validation
- Week/game enforcement logic
- Series types for each playoff round
This complements the config.py changes from commit aad9c00.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
570 lines
16 KiB
Markdown
570 lines
16 KiB
Markdown
# Injury Commands
|
|
|
|
**Command Group:** `/injury`
|
|
**Permission Required:** SBA Players role (for set-new and clear)
|
|
**Subcommands:** roll, set-new, clear
|
|
|
|
## Overview
|
|
|
|
The injury command family provides comprehensive player injury management for the SBA league. Team managers can roll for injuries using official Strat-o-Matic injury tables, record confirmed injuries, and clear injuries when players return.
|
|
|
|
## Commands
|
|
|
|
### `/injury roll`
|
|
|
|
Roll for injury based on a player's injury rating using 3d6 dice and official injury tables.
|
|
|
|
**Usage:**
|
|
```
|
|
/injury roll <player_name>
|
|
```
|
|
|
|
**Parameters:**
|
|
- `player_name` (required, autocomplete): Name of the player - uses smart autocomplete prioritizing your team's players
|
|
|
|
**Injury Rating Format:**
|
|
The player's `injury_rating` field contains both the games played and rating in format `#p##`:
|
|
- **Format**: `1p70`, `4p50`, `2p65`, etc.
|
|
- **First character**: Games played in current series (1-6)
|
|
- **Remaining characters**: Injury rating (p70, p65, p60, p50, p40, p30, p20)
|
|
|
|
**Examples:**
|
|
- `1p70` = 1 game played, p70 rating
|
|
- `4p50` = 4 games played, p50 rating
|
|
- `2p65` = 2 games played, p65 rating
|
|
|
|
**Dice Roll:**
|
|
- Rolls 3d6 (3-18 range)
|
|
- Automatically extracts games played and rating from player's injury_rating field
|
|
- Looks up result in official Strat-o-Matic injury tables
|
|
- Returns injury duration based on rating and games played
|
|
|
|
**Possible Results:**
|
|
- **OK**: No injury
|
|
- **REM**: Remainder of game (batters) or Fatigued (pitchers)
|
|
- **Number**: Games player will miss (1-24 games)
|
|
|
|
**Example:**
|
|
```
|
|
/injury roll Mike Trout
|
|
```
|
|
|
|
**Response Fields:**
|
|
- **Roll**: Total rolled and individual dice (e.g., "15 (3d6: 5 + 5 + 5)")
|
|
- **Player**: Player name and position
|
|
- **Injury Rating**: Full rating with parsed details (e.g., "4p50 (p50, 4 games)")
|
|
- **Result**: Injury outcome (OK, REM, or number of games)
|
|
- **Team**: Player's current team
|
|
|
|
**Response Colors:**
|
|
- **Green**: OK (no injury)
|
|
- **Gold**: REM (remainder of game/fatigued)
|
|
- **Orange**: Number of games (injury occurred)
|
|
|
|
**Error Handling:**
|
|
If a player's `injury_rating` is not in the correct format, an error message will be displayed:
|
|
```
|
|
Invalid Injury Rating Format
|
|
{Player} has an invalid injury rating: `{rating}`
|
|
|
|
Expected format: #p## (e.g., 1p70, 4p50)
|
|
```
|
|
|
|
---
|
|
|
|
### `/injury set-new`
|
|
|
|
Record a new injury for a player on your team.
|
|
|
|
**Usage:**
|
|
```
|
|
/injury set-new <player_name> <this_week> <this_game> <injury_games>
|
|
```
|
|
|
|
**Parameters:**
|
|
- `player_name` (required): Name of the player to injure
|
|
- `this_week` (required): Current week number (1-21, including playoffs)
|
|
- `this_game` (required): Current game number (varies by week - see below)
|
|
- `injury_games` (required): Total number of games player will be out
|
|
|
|
**Week and Game Validation:**
|
|
- **Regular Season (Weeks 1-18)**: Game number must be 1-4
|
|
- **Playoff Round 1 (Week 19)**: Game number must be 1-5 (best of 5 series)
|
|
- **Playoff Round 2 (Week 20)**: Game number must be 1-7 (best of 7 series)
|
|
- **Playoff Round 3 (Week 21)**: Game number must be 1-7 (best of 7 series)
|
|
|
|
**Additional Validation:**
|
|
- Player must exist in current season
|
|
- Player cannot already have an active injury
|
|
- Injury duration must be at least 1 game
|
|
|
|
**Automatic Calculations:**
|
|
The command automatically calculates:
|
|
1. Injury start date (adjusts for final game of series edge case)
|
|
2. Return date based on injury duration
|
|
3. Week rollover when games exceed the max for that week (4 regular season, 5-7 playoffs)
|
|
|
|
**Examples:**
|
|
|
|
Regular Season:
|
|
```
|
|
/injury set-new Mike Trout 5 2 4
|
|
```
|
|
This records an injury occurring in week 5, game 2, with player out for 4 games (returns week 6, game 2).
|
|
|
|
Playoffs:
|
|
```
|
|
/injury set-new Shohei Ohtani 19 3 2
|
|
```
|
|
This records an injury in playoff round 1 (week 19), game 3, with player out for 2 games (returns week 19, game 5).
|
|
|
|
```
|
|
/injury set-new Aaron Judge 20 5 8
|
|
```
|
|
This records an injury in playoff round 2 (week 20), game 5, with player out for 8 games (spans multiple weeks).
|
|
|
|
**Response:**
|
|
- Confirmation embed with injury details
|
|
- Player's name, position, and team
|
|
- Total games missed
|
|
- Calculated return date
|
|
|
|
---
|
|
|
|
### `/injury clear`
|
|
|
|
Clear a player's active injury and mark them as eligible to play.
|
|
|
|
**Usage:**
|
|
```
|
|
/injury clear <player_name>
|
|
```
|
|
|
|
**Parameters:**
|
|
- `player_name` (required, autocomplete): Name of the player whose injury to clear - uses smart autocomplete prioritizing your team's players
|
|
|
|
**Validation:**
|
|
- Player must exist in current season
|
|
- Player must have an active injury
|
|
|
|
**User Flow:**
|
|
1. Command issued with player name
|
|
2. **Confirmation embed displayed** showing:
|
|
- Player name and position
|
|
- Team name and abbreviation
|
|
- Expected return date
|
|
- Total games missed
|
|
- Team thumbnail (if available)
|
|
3. User prompted: "Is {Player Name} cleared to return?"
|
|
4. Two buttons presented:
|
|
- **"Clear Injury"** → Proceeds with clearing the injury
|
|
- **"Cancel"** → Cancels the operation
|
|
5. After confirmation, injury is cleared and success message displayed
|
|
|
|
**Example:**
|
|
```
|
|
/injury clear Mike Trout
|
|
```
|
|
|
|
**Confirmation Embed:**
|
|
- Title: Player name
|
|
- Description: "Is **{Player Name}** cleared to return?"
|
|
- Fields: Player info, team, expected return date, games missed
|
|
- Buttons: "Clear Injury" / "Cancel"
|
|
- Timeout: 3 minutes
|
|
|
|
**Success Response (after confirmation):**
|
|
- Confirmation that injury was cleared
|
|
- Shows previous return date
|
|
- Shows total games that were missed
|
|
- Player's team information
|
|
|
|
**Responders:**
|
|
- Command issuer
|
|
- Team GM(s) - can also confirm/cancel on behalf of team
|
|
|
|
---
|
|
|
|
## Date Format
|
|
|
|
All injury dates use the format `w##g#`:
|
|
- `w##` = Week number (zero-padded to 2 digits)
|
|
- `g#` = Game number (1-4)
|
|
|
|
**Examples:**
|
|
- `w05g2` = Week 5, Game 2
|
|
- `w12g4` = Week 12, Game 4
|
|
- `w01g1` = Week 1, Game 1
|
|
|
|
## Injury Calculation Logic
|
|
|
|
### Basic Calculation
|
|
|
|
For an injury of N games starting at week W, game G:
|
|
|
|
1. **Calculate weeks and remaining games:**
|
|
```
|
|
out_weeks = floor(N / 4)
|
|
out_games = N % 4
|
|
```
|
|
|
|
2. **Calculate return date:**
|
|
```
|
|
return_week = W + out_weeks
|
|
return_game = G + 1 + out_games
|
|
```
|
|
|
|
3. **Handle week rollover:**
|
|
```
|
|
if return_game > 4:
|
|
return_week += 1
|
|
return_game -= 4
|
|
```
|
|
|
|
### Special Cases
|
|
|
|
#### Game 4 Edge Case
|
|
If injury occurs during game 4, the start date is adjusted:
|
|
```
|
|
start_week = W + 1
|
|
start_game = 1
|
|
```
|
|
|
|
#### Examples
|
|
|
|
**Example 1: Simple injury (same week)**
|
|
- Current: Week 5, Game 1
|
|
- Injury: 2 games
|
|
- Return: Week 5, Game 4
|
|
|
|
**Example 2: Week rollover**
|
|
- Current: Week 5, Game 3
|
|
- Injury: 3 games
|
|
- Return: Week 6, Game 3
|
|
|
|
**Example 3: Multi-week injury**
|
|
- Current: Week 5, Game 2
|
|
- Injury: 8 games
|
|
- Return: Week 7, Game 3
|
|
|
|
**Example 4: Game 4 start**
|
|
- Current: Week 5, Game 4
|
|
- Injury: 2 games
|
|
- Start: Week 6, Game 1
|
|
- Return: Week 6, Game 3
|
|
|
|
## Database Schema
|
|
|
|
### Injury Model
|
|
|
|
```python
|
|
class Injury(SBABaseModel):
|
|
id: int # Injury ID
|
|
season: int # Season number
|
|
player_id: int # Player ID
|
|
total_games: int # Total games player will be out
|
|
start_week: int # Week injury started
|
|
start_game: int # Game number injury started (1-4)
|
|
end_week: int # Week player returns
|
|
end_game: int # Game number player returns (1-4)
|
|
is_active: bool # Whether injury is currently active
|
|
```
|
|
|
|
### API Integration
|
|
|
|
The commands interact with the following API endpoints:
|
|
|
|
- `GET /api/v3/injuries` - Query injuries with filters
|
|
- `POST /api/v3/injuries` - Create new injury record
|
|
- `PATCH /api/v3/injuries/{id}` - Update injury (clear active status)
|
|
- `PATCH /api/v3/players/{id}` - Update player's il_return field
|
|
|
|
## Service Layer
|
|
|
|
### InjuryService
|
|
|
|
**Location:** `services/injury_service.py`
|
|
|
|
**Key Methods:**
|
|
- `get_active_injury(player_id, season)` - Get active injury for player
|
|
- `get_injuries_by_player(player_id, season, active_only)` - Get all injuries for player
|
|
- `get_injuries_by_team(team_id, season, active_only)` - Get team injuries
|
|
- `create_injury(...)` - Create new injury record
|
|
- `clear_injury(injury_id)` - Deactivate injury
|
|
|
|
## Permissions
|
|
|
|
### Required Roles
|
|
|
|
**For `/injury check`:**
|
|
- No role required (available to all users)
|
|
|
|
**For `/injury set-new` and `/injury clear`:**
|
|
- **SBA Players** role required
|
|
- Configured via `SBA_PLAYERS_ROLE_NAME` environment variable
|
|
|
|
### Permission Checks
|
|
|
|
The commands use `has_player_role()` method to verify user has appropriate role:
|
|
|
|
```python
|
|
def has_player_role(self, interaction: discord.Interaction) -> bool:
|
|
"""Check if user has the SBA Players role."""
|
|
player_role = discord.utils.get(
|
|
interaction.guild.roles,
|
|
name=get_config().sba_players_role_name
|
|
)
|
|
return player_role in interaction.user.roles if player_role else False
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
### Common Errors
|
|
|
|
**Player Not Found:**
|
|
```
|
|
❌ Player Not Found
|
|
I did not find anybody named **{player_name}**.
|
|
```
|
|
|
|
**Already Injured:**
|
|
```
|
|
❌ Already Injured
|
|
Hm. It looks like {player_name} is already hurt.
|
|
```
|
|
|
|
**Not Injured:**
|
|
```
|
|
❌ No Active Injury
|
|
{player_name} isn't injured.
|
|
```
|
|
|
|
**Invalid Input:**
|
|
```
|
|
❌ Invalid Input
|
|
Game number must be between 1 and 4.
|
|
```
|
|
|
|
**Permission Denied:**
|
|
```
|
|
❌ Permission Denied
|
|
This command requires the **SBA Players** role.
|
|
```
|
|
|
|
## Logging
|
|
|
|
All injury commands use the `@logged_command` decorator for automatic logging:
|
|
|
|
```python
|
|
@app_commands.command(name="check")
|
|
@logged_command("/injury check")
|
|
async def injury_check(self, interaction, player_name: str):
|
|
# Command implementation
|
|
```
|
|
|
|
**Log Context:**
|
|
- Command name
|
|
- User ID and username
|
|
- Player name
|
|
- Season
|
|
- Injury details (duration, dates)
|
|
- Success/failure status
|
|
|
|
**Example Log:**
|
|
```json
|
|
{
|
|
"level": "INFO",
|
|
"command": "/injury set-new",
|
|
"user_id": "123456789",
|
|
"player_name": "Mike Trout",
|
|
"season": 12,
|
|
"injury_games": 4,
|
|
"return_date": "w06g2",
|
|
"message": "Injury set for Mike Trout"
|
|
}
|
|
```
|
|
|
|
## Testing
|
|
|
|
### Test Coverage
|
|
|
|
**Location:** `tests/test_services_injury.py`
|
|
|
|
**Test Categories:**
|
|
1. **Model Tests** (5 tests) - Injury model creation and properties
|
|
2. **Service Tests** (8 tests) - InjuryService CRUD operations with API mocking
|
|
3. **Roll Logic Tests** (8 tests) - Injury rating parsing, table lookup, and dice roll logic
|
|
4. **Calculation Tests** (5 tests) - Date calculation logic for injury duration
|
|
|
|
**Total:** 26 comprehensive tests
|
|
|
|
**Running Tests:**
|
|
```bash
|
|
# Run all injury tests
|
|
python -m pytest tests/test_services_injury.py -v
|
|
|
|
# Run specific test class
|
|
python -m pytest tests/test_services_injury.py::TestInjuryService -v
|
|
python -m pytest tests/test_services_injury.py::TestInjuryRollLogic -v
|
|
|
|
# Run with coverage
|
|
python -m pytest tests/test_services_injury.py --cov=services.injury_service --cov=commands.injuries
|
|
```
|
|
|
|
## Injury Roll Tables
|
|
|
|
### Table Structure
|
|
|
|
The injury tables are based on official Strat-o-Matic rules with the following structure:
|
|
|
|
**Ratings:** p70, p65, p60, p50, p40, p30, p20 (higher is better)
|
|
**Games Played:** 1-6 games in current series
|
|
**Roll:** 3d6 (results from 3-18)
|
|
|
|
### Rating Availability by Games Played
|
|
|
|
Not all ratings are available for all games played combinations:
|
|
|
|
- **1 game**: All ratings (p70-p20)
|
|
- **2 games**: All ratings (p70-p20)
|
|
- **3 games**: p65-p20 (p70 exempt)
|
|
- **4 games**: p60-p20 (p70, p65 exempt)
|
|
- **5 games**: p60-p20 (p70, p65 exempt)
|
|
- **6 games**: p40-p20 (p70, p65, p60, p50 exempt)
|
|
|
|
When a rating/games combination has no table, the result is automatically "OK" (no injury).
|
|
|
|
### Example Table (p65, 1 game):
|
|
|
|
| Roll | Result |
|
|
|------|--------|
|
|
| 3 | 2 |
|
|
| 4 | 2 |
|
|
| 5 | OK |
|
|
| 6 | REM |
|
|
| 7 | 1 |
|
|
| ... | ... |
|
|
| 18 | 12 |
|
|
|
|
## UI/UX Design
|
|
|
|
### Embed Colors
|
|
|
|
- **Roll (OK):** Green - No injury
|
|
- **Roll (REM):** Gold - Remainder of game/Fatigued
|
|
- **Roll (Injury):** Orange - Number of games
|
|
- **Set New:** Success (green) - `EmbedTemplate.success()`
|
|
- **Clear:** Success (green) - `EmbedTemplate.success()`
|
|
- **Errors:** Error (red) - `EmbedTemplate.error()`
|
|
|
|
### Response Format
|
|
|
|
All successful responses use Discord embeds with:
|
|
- Clear title indicating action/status
|
|
- Well-organized field layout
|
|
- Team information when applicable
|
|
- Consistent formatting for dates
|
|
|
|
## Integration with Player Model
|
|
|
|
The Player model includes injury-related fields:
|
|
|
|
```python
|
|
class Player(SBABaseModel):
|
|
# ... other fields ...
|
|
pitcher_injury: Optional[int] # Pitcher injury rating
|
|
injury_rating: Optional[str] # General injury rating
|
|
il_return: Optional[str] # Injured list return date (w##g#)
|
|
```
|
|
|
|
When an injury is set or cleared, the player's `il_return` field is automatically updated via PlayerService.
|
|
|
|
## Future Enhancements
|
|
|
|
Possible improvements for future versions:
|
|
|
|
1. **Injury History** - View player's injury history for a season
|
|
2. **Team Injury Report** - List all injuries for a team
|
|
3. **Injury Notifications** - Automatic notifications when players return from injury
|
|
4. **Injury Statistics** - Track injury trends and statistics
|
|
5. **Injury Chart Image** - Display the official injury chart as an embed image
|
|
|
|
## Migration from Legacy
|
|
|
|
### Legacy Commands
|
|
|
|
The legacy injury commands were located in:
|
|
- `discord-app/cogs/players.py` - `set_injury_slash()` and `clear_injury_slash()`
|
|
- `discord-app/cogs/players.py` - `injury_roll_slash()` with manual rating/games input
|
|
|
|
## Configuration
|
|
|
|
The injury system uses configuration constants from `config.py` to validate week and game numbers:
|
|
|
|
### Season Structure
|
|
```python
|
|
weeks_per_season: int = 18 # Regular season weeks
|
|
games_per_week: int = 4 # Games per week in regular season
|
|
playoff_weeks_per_season: int = 3 # Playoff weeks (19-21)
|
|
playoff_round_one_games: int = 5 # Week 19: Best of 5
|
|
playoff_round_two_games: int = 7 # Week 20: Best of 7
|
|
playoff_round_three_games: int = 7 # Week 21: Best of 7
|
|
```
|
|
|
|
These constants are used in:
|
|
- **`views/modals.py`**: `InjuryRollModal` and `ClearInjuryModal` for week/game validation
|
|
- **Validation Logic**: Ensures proper game number ranges based on week (regular season vs playoff rounds)
|
|
|
|
### Week/Game Limits
|
|
The injury system automatically enforces proper limits based on the week:
|
|
|
|
| Week Range | Season Phase | Max Games | Series Type |
|
|
|------------|--------------|-----------|-------------|
|
|
| 1-18 | Regular | 4 | N/A |
|
|
| 19 | Playoff R1 | 5 | Best of 5 |
|
|
| 20 | Playoff R2 | 7 | Best of 7 |
|
|
| 21 | Playoff R3 | 7 | Best of 7 |
|
|
|
|
### Key Improvements
|
|
|
|
1. **Cleaner Command Structure:** Using GroupCog for organized subcommands (`/injury roll`, `/injury set-new`, `/injury clear`)
|
|
2. **Simplified Interface:** Single parameter for injury roll - games played automatically extracted from player data
|
|
3. **Smart Injury Ratings:** Automatically reads and parses player's injury rating from database
|
|
4. **Player Autocomplete:** Modern autocomplete with team prioritization for better UX
|
|
5. **Better Error Handling:** User-friendly error messages via EmbedTemplate with format validation
|
|
6. **Improved Logging:** Automatic logging via @logged_command decorator
|
|
7. **Service Layer:** Separated business logic from command handlers
|
|
8. **Type Safety:** Full type hints and Pydantic models
|
|
9. **Testability:** Comprehensive unit tests (26 tests) with mocked API calls
|
|
10. **Modern UI:** Consistent embed-based responses with color coding
|
|
11. **Official Tables:** Complete Strat-o-Matic injury tables built into the command
|
|
12. **Playoff Support:** Full support for injury rolls during playoff weeks with series-specific game limits (October 2025)
|
|
|
|
### Migration Details
|
|
|
|
**Old:** `/injuryroll <rating> <games>` - Manual rating and games selection
|
|
**New:** `/injury roll <player>` - Single parameter, automatic rating and games extraction from player's `injury_rating` field
|
|
|
|
**Old:** `/setinjury <player> <week> <game> <duration>`
|
|
**New:** `/injury set-new <player> <week> <game> <duration>` - Same functionality, better naming
|
|
|
|
**Old:** `/clearinjury <player>`
|
|
**New:** `/injury clear <player>` - Same functionality, better naming
|
|
|
|
### Database Field Update
|
|
|
|
The `injury_rating` field format has changed to include games played:
|
|
- **Old Format**: `p65`, `p70`, etc. (rating only)
|
|
- **New Format**: `1p70`, `4p50`, `2p65`, etc. (games + rating)
|
|
|
|
Players must have their `injury_rating` field updated to the new format for the `/injury roll` command to work.
|
|
|
|
---
|
|
|
|
**Last Updated:** October 2025
|
|
**Version:** 2.0
|
|
**Status:** Active
|
|
|
|
**Recent Updates:**
|
|
- **October 2025**: Added playoff support with series-specific game validation (weeks 19-21)
|