# Story 2.11: Progress Feedback Temps Reel

Status: done

## Story

As a **user**,
I want **to see real-time progress during translation**,
So that **I know the status of my translation**.

## Acceptance Criteria

1. **AC1: Progress Endpoint** - GET /api/v1/translations/{id} returns `{data: {id, status, progress_percent, current_step}, meta: {...}}`
2. **AC2: Real-time Latency** - Progress updates within 500ms of actual progress (NFR3)
3. **AC3: Status Values** - Status can be: "queued", "processing", "completed", "failed"
4. **AC4: LLM Progress Details** - For LLM mode, progress shows "translating slide X/Y" or "processing cell X/Y"
5. **AC5: Progress Percentage** - progress_percent field shows 0-100 based on completion
6. **AC6: Current Step** - current_step field describes what's being processed (e.g., "Validating file", "Translating sheet 1", "Generating output")
7. **AC7: Error State** - Failed translations return status "failed" with error_message in response
8. **AC8: Job Not Found** - Invalid job ID returns 404 with error "NOT_FOUND"

## Tasks / Subtasks

- [x] **Task 1: Extend Job Data Model** (AC: 1, 5, 6)
  - [x] 1.1 Add `progress_percent` (int 0-100) to job dict
  - [x] 1.2 Add `current_step` (str) to job dict
  - [x] 1.3 Add `total_items` and `processed_items` for granular tracking
  - [x] 1.4 Add `error_message` field for failed jobs

- [x] **Task 2: Create Progress Tracker Service** (AC: 2, 4)
  - [x] 2.1 Create `ProgressTracker` class with callback mechanism
  - [x] 2.2 Implement `update_progress(job_id, percent, step)` method
  - [x] 2.3 Support item-based progress: `update_item(job_id, current, total, item_name)`
  - [x] 2.4 Store progress in Redis or in-memory dict (consistent with existing pattern)

- [x] **Task 3: Update Translation Processors** (AC: 4)
  - [x] 3.1 Modify `excel_translator.py` to report progress per sheet/cell batch
  - [x] 3.2 Modify `word_translator.py` to report progress per paragraph/section
  - [x] 3.3 Modify `pptx_translator.py` to report progress per slide
  - [x] 3.4 Add `progress_callback` parameter to translator functions

- [x] **Task 4: Update Translation Job Processor** (AC: 2, 4)
  - [x] 4.1 Modify `_run_translation_job()` to use ProgressTracker
  - [x] 4.2 Set initial status "queued" before processing starts
  - [x] 4.3 Update to "processing" with progress during translation
  - [x] 4.4 Pass progress_callback to translators

- [x] **Task 5: Enhance Status Endpoint** (AC: 1, 3, 7, 8)
  - [x] 5.1 Update `GET /api/v1/translations/{job_id}` to include all new fields
  - [x] 5.2 Return proper 404 for non-existent jobs
  - [x] 5.3 Include error_message for failed status
  - [x] 5.4 Add response schema with all fields documented

- [x] **Task 6: Create Response Schemas** (AC: 1)
  - [x] 6.1 Create `TranslationStatusResponse` Pydantic model
  - [x] 6.2 Create `TranslationStatusData` with all progress fields
  - [x] 6.3 Create `TranslationStatusMeta` for metadata

- [x] **Task 7: Add Unit Tests** (AC: All)
  - [x] 7.1 Test progress updates are reflected correctly
  - [x] 7.2 Test status transitions (queued → processing → completed/failed)
  - [x] 7.3 Test 404 for non-existent job
  - [x] 7.4 Test error state includes error_message
  - [x] 7.5 Test LLM mode progress shows item details

- [x] **Task 8: Update OpenAPI Documentation** (AC: All)
  - [x] 8.1 Document all response fields
  - [x] 8.2 Add example responses for each status
  - [x] 8.3 Document error codes

## Dev Notes

### Previous Story Intelligence (Story 2.10)

**Existing Job Storage Pattern:**
```python
# In translate_routes.py
_translation_jobs: dict[str, dict] = {}

# Job structure (current):
_translation_jobs[job_id] = {
    "id": job_id,
    "status": "processing",
    "file_name": original_filename,
    "source_lang": source_lang,
    "target_lang": target_lang,
    "created_at": datetime.now(timezone.utc).isoformat(),
    "user_id": user_id,
    "input_path": str(input_path),
    "file_extension": file_extension,
    "provider": provider or mode,
    "webhook_url": webhook_url,
    "custom_prompt": custom_prompt,
    "glossary_id": glossary_id,
}
```

**Existing Status Endpoint (needs enhancement):**
```python
@router_v1.get("/translations/{job_id}")
async def get_translation_status(job_id: str, ...):
    job = _translation_jobs.get(job_id)
    if not job:
        return JSONResponse(status_code=404, ...)
    # Currently returns: id, status, file_name, source_lang, target_lang, created_at
    # NEEDS: progress_percent, current_step, error_message
```

**Background Processing Pattern:**
```python
asyncio.create_task(
    _run_translation_job(
        job_id=job_id,
        input_path=input_path,
        ...
    )
)
```

### Architecture Compliance

Per `_bmad-output/planning-artifacts/architecture.md`:

**Response Format (Success):**
```json
{
  "data": {
    "id": "tr_abc123",
    "status": "processing",
    "progress_percent": 45,
    "current_step": "Translating sheet 2/5",
    "file_name": "report.xlsx",
    "source_lang": "en",
    "target_lang": "fr",
    "created_at": "2024-01-15T10:30:00Z"
  },
  "meta": {
    "estimated_remaining_seconds": 8
  }
}
```

**Response Format (Completed):**
```json
{
  "data": {
    "id": "tr_abc123",
    "status": "completed",
    "progress_percent": 100,
    "current_step": "Translation complete",
    "file_name": "report.xlsx",
    "created_at": "2024-01-15T10:30:00Z",
    "completed_at": "2024-01-15T10:30:45Z"
  },
  "meta": {}
}
```

**Response Format (Failed):**
```json
{
  "data": {
    "id": "tr_abc123",
    "status": "failed",
    "progress_percent": 30,
    "current_step": "Error during translation",
    "error_message": "Provider unavailable: timeout after 30s",
    "file_name": "report.xlsx",
    "created_at": "2024-01-15T10:30:00Z",
    "failed_at": "2024-01-15T10:30:15Z"
  },
  "meta": {}
}
```

**Response Format (Not Found):**
```json
{
  "error": "NOT_FOUND",
  "message": "Job de traduction non trouve.",
  "details": {"job_id": "tr_nonexistent"}
}
```

### Naming Conventions (Per Architecture)

| Element | Convention | Example |
|---------|------------|---------|
| Variables | snake_case | `progress_percent`, `current_step` |
| JSON fields | snake_case | `progress_percent`, `current_step`, `error_message` |
| Classes | PascalCase | `ProgressTracker`, `TranslationStatusResponse` |
| Functions | snake_case | `update_progress()`, `get_translation_status()` |

### Implementation Approach

**ProgressTracker Class Design:**
```python
class ProgressTracker:
    """Track translation progress with callback support."""
    
    def __init__(self, job_id: str, storage: dict):
        self.job_id = job_id
        self.storage = storage  # Reference to _translation_jobs
    
    def update(self, percent: int, step: str) -> None:
        """Update progress percentage and current step."""
        job = self.storage.get(self.job_id)
        if job:
            job["progress_percent"] = min(100, max(0, percent))
            job["current_step"] = step
    
    def update_item(self, current: int, total: int, item_name: str) -> None:
        """Update progress based on item count (e.g., slides, sheets)."""
        percent = int((current / total) * 100) if total > 0 else 0
        step = f"{item_name} {current}/{total}"
        self.update(percent, step)
```

**Translator Integration Pattern:**
```python
# In translators/excel_translator.py
def translate_file(
    self, 
    input_path: Path, 
    output_path: Path, 
    target_lang: str, 
    source_lang: str = "auto",
    progress_callback: Optional[Callable[[int, str], None]] = None
) -> None:
    # ... validation ...
    
    total_sheets = len(workbook.sheetnames)
    for idx, sheet_name in enumerate(workbook.sheetnames):
        # Process sheet...
        if progress_callback:
            progress = int((idx + 1) / total_sheets * 100)
            progress_callback(progress, f"Translating sheet {idx + 1}/{total_sheets}")
```

### Project Structure Notes

**Files to Modify:**
- `routes/translate_routes.py` - Update job storage, status endpoint
- `translators/excel_translator.py` - Add progress callback
- `translators/word_translator.py` - Add progress callback
- `translators/pptx_translator.py` - Add progress callback

**Files to Create:**
- `services/progress_tracker.py` - ProgressTracker class (optional, can be inline)

### Git Intelligence - Recent Patterns

From recent commits:
- Translation cache implemented (5000 entry LRU cache)
- OpenRouter provider with DeepSeek support
- Parallel processing optimizations
- Redis sessions for production

### Dependencies on Previous Stories

| Story | Dependency |
|-------|------------|
| 2.10 | Job storage pattern (`_translation_jobs` dict) |
| 2.7 | ExcelProcessor structure for progress hooks |
| 2.8 | WordProcessor structure for progress hooks |
| 2.9 | PowerPointProcessor structure for progress hooks |

### Anti-Patterns to Avoid

1. **Don't use blocking calls** - Progress updates must be fast, non-blocking
2. **Don't exceed 500ms latency** - NFR3 requirement
3. **Don't log file content** - Only progress metadata (NFR11, NFR16)
4. **Don't break existing API contract** - Extend, don't modify existing response fields
5. **Don't create tight coupling** - Progress callback should be optional parameter

### Performance Considerations

- Progress updates should be O(1) - simple dict updates
- Avoid polling Redis on every progress update if using Redis
- Consider throttling updates (max 10 per second) for high-frequency operations
- Use in-memory dict for MVP (already established pattern in Story 2.10)

### Testing Strategy

```bash
# Unit tests
pytest tests/test_progress_tracking.py -v

# Integration tests
pytest tests/test_translate_endpoint.py -v -k "progress"

# With coverage
pytest tests/ --cov=routes/translate_routes --cov=translators -v
```

### References

- [Source: _bmad-output/planning-artifacts/epics.md#Story 2.11]
- [Source: _bmad-output/planning-artifacts/architecture.md#API Response Formats]
- [Source: _bmad-output/planning-artifacts/prd.md#FR47 Real-time progress]
- [Source: _bmad-output/planning-artifacts/prd.md#NFR3 Progress latency < 500ms]
- [Source: _bmad-output/implementation-artifacts/2-10-endpoint-post-api-v1-translate-core.md - Previous story]
- [Source: routes/translate_routes.py - Current job storage and status endpoint]
- [Source: services/translation_service.py - Translation batch processing pattern]

## Dev Agent Record

### Agent Model Used

glm-5

### Debug Log References

None

### Completion Notes List

1. **Task 1 Complete**: Extended job data model with progress_percent (0-100), current_step (str), total_items, processed_items, and error_message fields. Job status now starts as "queued" and transitions to "processing" when work begins.

2. **Task 2 Complete**: Created `services/progress_tracker.py` with ProgressTracker class. Includes `update()`, `update_item()`, `set_error()`, and `set_completed()` methods. Thread-safe with throttling (50ms min interval) to meet NFR3 <500ms latency requirement.

3. **Task 3 Complete**: All translators (excel, word, pptx) already have progress_callback support. Updated `_run_translation_job()` to create and pass progress callback to translators.

4. **Task 4 Complete**: Modified `_run_translation_job()` to use ProgressTracker. Sets initial status "queued", transitions to "processing", and passes progress_callback to translators. Uses `set_completed()` and `set_error()` for final states.

5. **Task 5 Complete**: Enhanced `GET /api/v1/translations/{job_id}` to include progress_percent, current_step, and error_message fields. Returns 404 for non-existent jobs with proper error format.

6. **Task 6 Complete**: Created Pydantic response schemas: TranslationStatusData, TranslationStatusMeta, TranslationStatusResponse with all progress fields documented.

7. **Task 7 Complete**: Created `tests/test_progress_tracking.py` with 18 comprehensive tests covering ProgressTracker class, job data model, status endpoint behavior, status transitions, and LLM progress details. All 18 tests pass.

8. **Task 8 Complete**: Added comprehensive OpenAPI documentation in the status endpoint docstring with example responses for each status (processing, completed, failed, not found).

### File List

**Created files:**
- `services/progress_tracker.py` - ProgressTracker class with update(), update_item(), set_error(), set_completed() methods
- `tests/test_progress_tracking.py` - 18 unit tests for progress tracking

**Modified files:**
- `routes/translate_routes.py` - Added progress fields to job creation, integrated ProgressTracker, enhanced status endpoint with new fields, added response schemas, job cleanup mechanism, estimated_remaining_seconds calculation
- `translators/excel_translator.py` - Standardized progress callback keys (current, total)
- `translators/word_translator.py` - Standardized progress callback keys, added granular progress during translation phase
- `translators/pptx_translator.py` - Standardized progress callback keys (current, total)

## Change Log

- 2026-02-21: Code Review Fixes - Fixed race condition in ProgressTracker throttling, added job cleanup mechanism (TTL 1h), implemented estimated_remaining_seconds calculation, standardized progress callback keys across all translators, added granular progress for Word translation, fixed type hints
- 2026-02-21: Implemented Story 2.11 - Real-time progress feedback with ProgressTracker service, extended job data model, enhanced status endpoint, comprehensive tests (18/18 passing)
