Major changes across backend, frontend, infrastructure: - Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud) - Admin panel: user management, pricing, settings - Glossary system with CSV import/export - Subscription and tier quota management - Security hardening (rate limiting, API key auth, path traversal fixes) - Docker compose for dev, prod, and IONOS deployment - Alembic migrations for new tables - Frontend: dashboard, pricing page, landing page, i18n (en/fr) - Test suite and verification scripts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
13 KiB
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
- AC1: Progress Endpoint - GET /api/v1/translations/{id} returns
{data: {id, status, progress_percent, current_step}, meta: {...}} - AC2: Real-time Latency - Progress updates within 500ms of actual progress (NFR3)
- AC3: Status Values - Status can be: "queued", "processing", "completed", "failed"
- AC4: LLM Progress Details - For LLM mode, progress shows "translating slide X/Y" or "processing cell X/Y"
- AC5: Progress Percentage - progress_percent field shows 0-100 based on completion
- AC6: Current Step - current_step field describes what's being processed (e.g., "Validating file", "Translating sheet 1", "Generating output")
- AC7: Error State - Failed translations return status "failed" with error_message in response
- AC8: Job Not Found - Invalid job ID returns 404 with error "NOT_FOUND"
Tasks / Subtasks
-
Task 1: Extend Job Data Model (AC: 1, 5, 6)
- 1.1 Add
progress_percent(int 0-100) to job dict - 1.2 Add
current_step(str) to job dict - 1.3 Add
total_itemsandprocessed_itemsfor granular tracking - 1.4 Add
error_messagefield for failed jobs
- 1.1 Add
-
Task 2: Create Progress Tracker Service (AC: 2, 4)
- 2.1 Create
ProgressTrackerclass with callback mechanism - 2.2 Implement
update_progress(job_id, percent, step)method - 2.3 Support item-based progress:
update_item(job_id, current, total, item_name) - 2.4 Store progress in Redis or in-memory dict (consistent with existing pattern)
- 2.1 Create
-
Task 3: Update Translation Processors (AC: 4)
- 3.1 Modify
excel_translator.pyto report progress per sheet/cell batch - 3.2 Modify
word_translator.pyto report progress per paragraph/section - 3.3 Modify
pptx_translator.pyto report progress per slide - 3.4 Add
progress_callbackparameter to translator functions
- 3.1 Modify
-
Task 4: Update Translation Job Processor (AC: 2, 4)
- 4.1 Modify
_run_translation_job()to use ProgressTracker - 4.2 Set initial status "queued" before processing starts
- 4.3 Update to "processing" with progress during translation
- 4.4 Pass progress_callback to translators
- 4.1 Modify
-
Task 5: Enhance Status Endpoint (AC: 1, 3, 7, 8)
- 5.1 Update
GET /api/v1/translations/{job_id}to include all new fields - 5.2 Return proper 404 for non-existent jobs
- 5.3 Include error_message for failed status
- 5.4 Add response schema with all fields documented
- 5.1 Update
-
Task 6: Create Response Schemas (AC: 1)
- 6.1 Create
TranslationStatusResponsePydantic model - 6.2 Create
TranslationStatusDatawith all progress fields - 6.3 Create
TranslationStatusMetafor metadata
- 6.1 Create
-
Task 7: Add Unit Tests (AC: All)
- 7.1 Test progress updates are reflected correctly
- 7.2 Test status transitions (queued → processing → completed/failed)
- 7.3 Test 404 for non-existent job
- 7.4 Test error state includes error_message
- 7.5 Test LLM mode progress shows item details
-
Task 8: Update OpenAPI Documentation (AC: All)
- 8.1 Document all response fields
- 8.2 Add example responses for each status
- 8.3 Document error codes
Dev Notes
Previous Story Intelligence (Story 2.10)
Existing Job Storage Pattern:
# 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):
@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:
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):
{
"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):
{
"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):
{
"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):
{
"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:
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:
# 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 endpointtranslators/excel_translator.py- Add progress callbacktranslators/word_translator.py- Add progress callbacktranslators/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
- Don't use blocking calls - Progress updates must be fast, non-blocking
- Don't exceed 500ms latency - NFR3 requirement
- Don't log file content - Only progress metadata (NFR11, NFR16)
- Don't break existing API contract - Extend, don't modify existing response fields
- 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
# 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
-
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.
-
Task 2 Complete: Created
services/progress_tracker.pywith ProgressTracker class. Includesupdate(),update_item(),set_error(), andset_completed()methods. Thread-safe with throttling (50ms min interval) to meet NFR3 <500ms latency requirement. -
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. -
Task 4 Complete: Modified
_run_translation_job()to use ProgressTracker. Sets initial status "queued", transitions to "processing", and passes progress_callback to translators. Usesset_completed()andset_error()for final states. -
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. -
Task 6 Complete: Created Pydantic response schemas: TranslationStatusData, TranslationStatusMeta, TranslationStatusResponse with all progress fields documented.
-
Task 7 Complete: Created
tests/test_progress_tracking.pywith 18 comprehensive tests covering ProgressTracker class, job data model, status endpoint behavior, status transitions, and LLM progress details. All 18 tests pass. -
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() methodstests/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 calculationtranslators/excel_translator.py- Standardized progress callback keys (current, total)translators/word_translator.py- Standardized progress callback keys, added granular progress during translation phasetranslators/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)