132 lines
5.5 KiB
Markdown
132 lines
5.5 KiB
Markdown
# Story 2.5: Deployment Target Assignment
|
|
|
|
## Status
|
|
Completed - All acceptance criteria met, 33/33 tests passing (includes tier1-only constraint)
|
|
|
|
## Story
|
|
**As a developer**, I want to assign deployment targets (site_deployment_id) to generated content during the content generation process based on job configuration, so that specific articles are assigned to specific sites while others remain unassigned for random template selection.
|
|
|
|
## Context
|
|
This story only assigns `site_deployment_id` to GeneratedContent records. Template selection is handled entirely by Story 2.4's existing logic:
|
|
- If `site_deployment_id` is set → Story 2.4 uses mapped/random template for that site
|
|
- If `site_deployment_id` is null → Story 2.4 uses random template (no config persistence)
|
|
|
|
## Acceptance Criteria
|
|
- The job configuration file supports an optional `deployment_targets` array containing site custom_hostnames
|
|
- **Only tier1 articles are assigned to deployment targets** - tier2, tier3, etc. always get `site_deployment_id = null`
|
|
- During tier1 content generation, each article is assigned a `site_deployment_id` based on its index:
|
|
- If `deployment_targets` has N sites, articles 0 through N-1 get assigned round-robin
|
|
- Articles N and beyond get `site_deployment_id = null`
|
|
- If `deployment_targets` is not specified, all articles get `site_deployment_id = null`
|
|
- The `site_deployment_id` is stored in the `GeneratedContent` record at creation time
|
|
- Invalid hostnames in `deployment_targets` cause graceful errors with clear messages
|
|
- Valid hostnames that don't exist in the database cause graceful errors
|
|
|
|
## Tasks / Subtasks
|
|
|
|
### 1. Update Job Configuration Schema
|
|
**Effort:** 1 story point
|
|
|
|
- [x] Add `deployment_targets` field (optional array of strings) to job config schema
|
|
- [x] Update job config validation to check deployment_targets is an array of strings
|
|
- [x] Update example job file in `jobs/` directory with the new field
|
|
|
|
### 2. Implement Target Resolution
|
|
**Effort:** 2 story points
|
|
|
|
- [x] Add `resolve_hostname_to_id(hostname: str) -> Optional[int]` helper function
|
|
- Query SiteDeployment table by custom_hostname
|
|
- Return site_deployment_id if found, None if not found
|
|
- [x] Add `validate_and_resolve_targets(hostnames: List[str]) -> dict` function
|
|
- Pre-validate all hostnames at job start (fail fast)
|
|
- Return dict mapping hostname → site_deployment_id
|
|
- Raise clear error if any hostname is invalid/not found
|
|
|
|
### 3. Implement Round-Robin Assignment
|
|
**Effort:** 2 story points
|
|
|
|
- [x] Add `assign_site_for_article(article_index: int, resolved_targets: dict) -> Optional[int]` function
|
|
- [x] If resolved_targets is empty: return None
|
|
- [x] If article_index < len(resolved_targets): return targets[article_index]
|
|
- [x] If article_index >= len(resolved_targets): return None
|
|
|
|
### 4. Integration with Content Generation Service
|
|
**Effort:** 2 story points
|
|
|
|
- [x] Update `src/generation/batch_processor.py` to parse `deployment_targets` from job config
|
|
- [x] Call validation function at job start (before generating any content)
|
|
- [x] For each article in batch:
|
|
- Call assignment function to get site_deployment_id
|
|
- Pass site_deployment_id to repository when creating GeneratedContent
|
|
- [x] Log assignment decisions at INFO level
|
|
|
|
### 5. Unit Tests
|
|
**Effort:** 2 story points
|
|
|
|
- [x] Test hostname resolution with valid hostnames
|
|
- [x] Test hostname resolution with invalid hostnames
|
|
- [x] Test round-robin assignment with 3 targets, 10 articles
|
|
- [x] Test assignment with no deployment_targets (all null)
|
|
- [x] Test validation errors for non-existent hostnames
|
|
- [x] Achieve >80% code coverage (100% achieved with 13 unit tests)
|
|
|
|
### 6. Integration Tests
|
|
**Effort:** 2 story points
|
|
|
|
- [x] Test full generation flow with deployment_targets specified
|
|
- [x] Test 10 articles with 3 targets: verify first 3 assigned, remaining 7 are null
|
|
- [x] Test with deployment_targets = null (all articles get null site_deployment_id)
|
|
- [x] Test error handling for invalid deployment targets
|
|
- [x] Verify site_deployment_id persisted correctly in database (9 integration tests)
|
|
|
|
## Dev Notes
|
|
|
|
### Example Job Config
|
|
```json
|
|
{
|
|
"job_name": "Multi-Site Launch",
|
|
"project_id": 2,
|
|
"deployment_targets": [
|
|
"www.domain1.com",
|
|
"www.domain2.com",
|
|
"www.domain3.com"
|
|
],
|
|
"tiers": [
|
|
{
|
|
"tier": 1,
|
|
"article_count": 10
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Assignment Example
|
|
Job with tier1 (10 articles) and tier2 (100 articles), 3 deployment targets:
|
|
|
|
**Tier1 articles:**
|
|
- Article 0 → www.domain1.com (site_deployment_id = 5)
|
|
- Article 1 → www.domain2.com (site_deployment_id = 8)
|
|
- Article 2 → www.domain3.com (site_deployment_id = 12)
|
|
- Articles 3-9 → null
|
|
|
|
**Tier2 articles:**
|
|
- All 100 articles → null (tier2+ never get deployment targets)
|
|
|
|
### Technical Decisions
|
|
1. **Tier restriction:** Only tier1 articles can be assigned to deployment targets; tier2/tier3 always get null
|
|
2. **Target identifier:** Only support custom_hostname (not numeric IDs)
|
|
3. **Validation timing:** Validate all targets at job start (fail fast)
|
|
4. **Overflow handling:** Simple - just assign null after targets exhausted
|
|
5. **Null handling:** No deployment_targets = all articles get null
|
|
|
|
### Dependencies
|
|
- **Story 1.6:** SiteDeployment table must exist
|
|
- **Story 2.3:** Content generation service must be functional
|
|
- **Story 2.4:** Template selection logic already handles null site_deployment_id
|
|
|
|
### Database Changes Required
|
|
None - `site_deployment_id` field already exists in GeneratedContent model (added in Story 2.4)
|
|
|
|
### Total Effort
|
|
11 story points
|