Major Features: - Complete Ferrero ↔ CreativeX mapping system with 93 brands - Automated Box.com folder monitoring service - Email notifications with score breakdowns - Database integration for result storage Mapping System (v2.0.0): - mappings.json: 93 brand mappings, 44+ channel mappings - core/mapping_resolver.py: Translates Ferrero codes to CreativeX format - scripts/validate_mappings.py: Validation tool for brand/channel support - scripts/generate_brand_mappings.py: Auto-mapping tool - scripts/download_reports.py: Scorecard PDF download tool - Updated scripts/upload.py: Integrated mapping validation - Updated scripts/check_status.py: Added detailed score display with guidelines Documentation: - Updated README.md: Complete user guide with mapping system - Updated STATUS.md: Production-ready status with test results - MAPPINGS_GUIDE.md: Complete mapping documentation - MAPPING_IMPLEMENTATION.md: Implementation summary - BRAND_MAPPINGS_REVIEW.md: Brand mapping validation guide - PRODUCTION_BRANDS_SUMMARY.md: Production brand catalog - PRODUCTION_MAPPING_COMPLETE.md: Mapping completion summary Automation Service (New): - creativex-automation/: Complete automated Box monitoring service - Monitors Box Ferrero-In folder (363284027140) for new files - Automatically uploads to CreativeX - Polls for completion (30 min intervals) - Extracts scores and stores in PostgreSQL creativex_scores table - Sends formatted emails to file uploader + daveporter@oliver.agency - Moves processed files to Processed subfolder Service Components: - automation/box_monitor.py: Box folder monitoring with uploader detection - automation/upload_processor.py: CreativeX upload integration - automation/status_poller.py: CreativeX status polling - automation/result_handler.py: Score extraction and email sending - automation/orchestrator.py: Service coordination - automation/processing_queue.py: JSON-based processing queue - service.py: Main service entry point - config.py: Service configuration loader - requirements.txt: All dependencies - deployment/systemd/: Systemd service unit file - Updated shared/notifier.py: Added creativex_upload_complete and creativex_upload_failed templates Testing: - Supports --dry-run mode for configuration testing - Supports --scan-once mode for Box folder testing - Manual run mode for development/testing - Comprehensive logging with rotation (10MB, 28 backups) Database Integration: - Uses existing creativex_scores table (no migrations needed) - Compatible with existing Ferrero-Opentext workflows - Stores full CreativeX API responses in JSONB Email Templates: - Matches Ferrero-Opentext styling (#9c27b0 purple for CreativeX) - Includes score, tier, guidelines breakdown, scorecard URL - Recipients: Box uploader + CC to daveporter@oliver.agency Deployment: - Runs locally for dev/testing - Systemd service for production - Auto-restart on failure - Complete documentation in creativex-automation/README.md Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
10 KiB
Creative X Mappings Guide
Overview
The Ferrero naming convention and Creative X API use different codes and naming for brands, channels, and markets. This mapping system bridges the two systems to enable seamless uploads.
Architecture
Ferrero Filename
↓
[Filename Parser] → Extracts Ferrero codes
↓
[Mapping Resolver] → Translates to Creative X format
↓
[API Client] → Uploads with Creative X metadata
Files
mappings.json- Complete mapping tables (brands, channels, countries)core/mapping_resolver.py- Python module for resolving mappingsscripts/upload.py- Upload script (uses mapping resolver)
Mapping Tables
1. Brand Mappings
Maps Ferrero brand codes to Creative X brand names and IDs.
Example:
{
"NUT": {
"creativex_name": "Nutella",
"creativex_id": 423,
"ferrero_name": "NUTELLA"
},
"RAF": {
"creativex_name": "Rafalleo",
"creativex_id": 422,
"ferrero_name": "RAFFAELLO"
}
}
Current Status:
- ✅ Nutella (NUT)
- ✅ Raffaello (RAF) - Note: Creative X spells it "Rafalleo"
- ❌ 103 other Ferrero brands not yet added to Creative X
2. Channel Mappings
Maps Ferrero social media codes to Creative X channel + publisher + placement combinations.
Structure:
{
"FERRERO_CODE": {
"ferrero_name": "Human-readable name",
"creativex_channel": "channel_name",
"creativex_publisher": "publisher_name (optional)",
"creativex_placement": "placement_name (optional)",
"creativex_ad_format": "ad_format (optional for YouTube/DV360)"
}
}
Examples:
Facebook (requires channel + publisher + placement)
FBS → facebook_paid / facebook / facebook_stories
IGF → instagram_paid / instagram / feed
YouTube (requires channel + publisher + ad_format)
YTB → google_ads / youtube / Bumper
YTS → google_ads / youtube / Shorts
Simple channels (only need channel name)
TIK → tiktok_paid
PIN → pinterest
Supported Channels:
| Ferrero Code | Creative X Channel | Notes |
|---|---|---|
| FBS, FBF, etc. | facebook_paid | 15 Facebook placements |
| IGF, IGR, IST, etc. | instagram_paid | 11 Instagram placements |
| MSI, MSS | facebook_paid | 2 Messenger placements |
| ANC, ANI, ANR | facebook_paid | 3 Audience Network placements |
| YTB, YTI, YTS, etc. | google_ads | 5 YouTube ad formats |
| DV3 | dv360 | Display & YouTube |
| TIK | tiktok_paid | TikTok |
| SNA | snapchat_paid | Snapchat |
| PIN | ||
| TWI | twitter_paid | Twitter/X |
| AMZ | amazon_paid | Amazon |
| GOO | google_ads | Generic Google |
3. Country Mappings
Most country codes use standard ISO 3166-1 alpha-2 codes and map directly.
Special Cases:
- GL = "Global" (not Greenland) - Creative X expects
market_name: "Global"with no ISO code - CZ = Czechia (Ferrero may use "Czech Republic")
- US = "United States of America" (not just "United States")
- GB = "United Kingdom"
- KR = "South Korea"
All other countries use standard ISO codes (DE, IT, FR, ES, etc.)
How It Works
1. Parsing Phase
from core.filename_parser import FerreroFilenameParser
parser = FerreroFilenameParser(data_loader)
parsed = parser.parse("NUT_GL_en_MOMENT_OLV_6S_1x1_FBS_xyz.mp4")
# Result:
{
'brand_code': 'NUT',
'brand_name': 'NUTELLA',
'country_code': 'GL',
'country_name': 'GLOBAL',
'language_code': 'en',
'social_media': 'FBS',
'channel': 'FB - Stories',
...
}
2. Mapping Phase
from core.mapping_resolver import MappingResolver
resolver = MappingResolver('mappings.json')
creativex_payload = resolver.build_creativex_payload(parsed)
# Result:
{
'brand_name': 'Nutella',
'channel': 'facebook_paid',
'publisher': 'facebook',
'placement': 'facebook_stories',
'market_name': 'Global',
'language': 'en',
'dimensions': '1x1',
'duration': '6',
...
}
3. Upload Phase
api_client.create_preflight({
'name': 'NUT_GL_en_MOMENT_OLV_6S_1x1_FBS_xyz.mp4',
'brand_name': 'Nutella',
'market_name': 'Global',
'channel': 'facebook_paid',
'publisher': 'facebook',
'placement': 'facebook_stories',
'language': 'en',
'dimensions': '1x1',
'duration': '6',
'creatives': [
{'source_url': 'https://s3.../file.mp4'}
]
})
Validation
The system validates at multiple levels:
1. Brand Validation
resolver.is_brand_supported('NUT') # True
resolver.is_brand_supported('ROC') # False - not in Creative X yet
2. Channel Validation
resolver.is_channel_supported('FBS') # True
resolver.is_channel_supported('XXX') # False - not mapped
3. Full Metadata Validation
is_valid, errors = resolver.validate_metadata_for_upload(parsed_data)
if not is_valid:
print("Errors:", errors)
Adding New Mappings
Add a New Brand
Edit mappings.json:
{
"brand_mappings": {
"ROC": {
"creativex_name": "Ferrero Rocher",
"creativex_id": 424,
"ferrero_name": "ROCHER"
}
}
}
Steps:
- Get Creative X brand ID from
/dimensionsendpoint - Add mapping to
mappings.json - Test upload:
python scripts/upload.py --dry-run test_file.mp4
Add a New Channel
Edit mappings.json:
{
"channel_mappings": {
"NEW_PLATFORM_CODES": {
"NPF": {
"ferrero_name": "New Platform - Feed",
"creativex_channel": "new_platform_paid",
"creativex_publisher": "new_platform",
"creativex_placement": "feed"
}
}
}
}
Steps:
- Query
/dimensionsto see Creative X channel structure - Determine if channel needs publisher/placement/ad_format
- Add mapping following existing patterns
- Test validation
Common Issues & Solutions
Issue: "Brand 'XXX' is not supported in Creative X"
Cause: Brand not yet added to Creative X system
Solutions:
- Check if brand exists:
python get_dimensions.py | grep -i brandname - If exists, add mapping to
mappings.json - If doesn't exist, contact Creative X to add brand
Issue: "Social media code 'XXX' not mapped"
Cause: Ferrero social code not in mappings.json
Solutions:
- Check Creative X channels:
python get_dimensions.py - Identify correct channel/publisher/placement
- Add mapping to
mappings.json
Issue: "Invalid placement for channel"
Cause: Mismatch between channel and placement
Solutions:
- Verify placement exists:
python get_dimensions.py - Check channel structure (some channels need publisher+placement, others don't)
- Update mapping with correct structure
Issue: "Market not found"
Cause: Country code mapping issue (usually GL = Global)
Solutions:
- Check if country exists:
python get_dimensions.py | grep -i country - For "Global" uploads, ensure GL maps to market_name="Global" (no ISO code)
- Update
country_mappings.SPECIAL_CASESif needed
Testing Mappings
1. Dry Run Test
Test mapping without uploading:
python scripts/upload.py --dry-run /path/to/test_file.mp4
Expected output:
✓ File valid
✓ Brand: NUTELLA
✓ Market: GLOBAL
✓ Channel: FB - Stories
✓ Metadata valid
✓ Mappings valid
→ Brand: Nutella
→ Channel: facebook_paid
→ Publisher: facebook
→ Placement: facebook_stories
✓ DRY RUN: Would upload file
2. Query Available Dimensions
Check what's available in Creative X:
source venv/bin/activate
python get_dimensions.py > dimensions_output.txt
Review:
- Available brands
- Available channels (with publishers/placements)
- Available markets
3. Interactive Python Test
from core.mapping_resolver import MappingResolver
resolver = MappingResolver('mappings.json')
# Test brand
brand = resolver.get_creativex_brand('NUT')
print(f"Brand: {brand}")
# Test channel
channel = resolver.get_creativex_channel_mapping('FBS')
print(f"Channel: {channel}")
# Test market
market = resolver.get_creativex_market('GL')
print(f"Market: {market}")
# List all supported
print(f"Supported brands: {resolver.get_all_supported_brands()}")
print(f"Supported channels: {resolver.get_all_supported_channels()}")
Quick Reference
Filename Format
BRAND_COUNTRY_LANG_SUBJECT_ASSETTYPE_DURATION_ASPECTRATIO_SOCIAL_hash.ext
NUT_GL_en_MOMENT_OLV_6S_1x1_FBS_xyz.mp4
│ │ │ │ │ │ │ │ └── Hash/random
│ │ │ │ │ │ │ └────── Social media code (FBS)
│ │ │ │ │ │ └────────── Aspect ratio (1x1)
│ │ │ │ │ └───────────── Duration (6S)
│ │ │ │ └───────────────── Asset type (OLV)
│ │ │ └──────────────────────── Subject (MOMENT)
│ │ └─────────────────────────── Language (en)
│ └────────────────────────────── Country (GL)
└────────────────────────────────── Brand (NUT)
Upload Workflow
1. Parse filename → Extract Ferrero codes
2. Validate file → Check format/size
3. Validate Ferrero metadata → Check against data.json
4. Validate mappings → Check brand/channel exist in Creative X
5. Get presigned URL → From Creative X API
6. Upload to S3 → Direct to S3
7. Create preflight → With mapped metadata
Support
Issues:
- Brand not mapped → Add to
brand_mappingsinmappings.json - Channel not mapped → Add to
channel_mappingsinmappings.json - Country not found → Check
country_mappings.SPECIAL_CASES
Testing:
# Test with dry run
python scripts/upload.py --dry-run file.mp4
# Check dimensions
python get_dimensions.py
# Check logs
cat data/logs/creativex_upload_*.log
Documentation:
- Main README:
README.md - Project status:
STATUS.md - This guide:
MAPPINGS_GUIDE.md