# 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 mappings - **`scripts/upload.py`** - Upload script (uses mapping resolver) --- ## Mapping Tables ### 1. Brand Mappings Maps Ferrero brand codes to Creative X brand names and IDs. **Example:** ```json { "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:** ```json { "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 | pinterest | Pinterest | | 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 ```python 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 ```python 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 ```python 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 ```python resolver.is_brand_supported('NUT') # True resolver.is_brand_supported('ROC') # False - not in Creative X yet ``` ### 2. Channel Validation ```python resolver.is_channel_supported('FBS') # True resolver.is_channel_supported('XXX') # False - not mapped ``` ### 3. Full Metadata Validation ```python 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`: ```json { "brand_mappings": { "ROC": { "creativex_name": "Ferrero Rocher", "creativex_id": 424, "ferrero_name": "ROCHER" } } } ``` **Steps:** 1. Get Creative X brand ID from `/dimensions` endpoint 2. Add mapping to `mappings.json` 3. Test upload: `python scripts/upload.py --dry-run test_file.mp4` ### Add a New Channel Edit `mappings.json`: ```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:** 1. Query `/dimensions` to see Creative X channel structure 2. Determine if channel needs publisher/placement/ad_format 3. Add mapping following existing patterns 4. Test validation --- ## Common Issues & Solutions ### Issue: "Brand 'XXX' is not supported in Creative X" **Cause:** Brand not yet added to Creative X system **Solutions:** 1. Check if brand exists: `python get_dimensions.py | grep -i brandname` 2. If exists, add mapping to `mappings.json` 3. 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:** 1. Check Creative X channels: `python get_dimensions.py` 2. Identify correct channel/publisher/placement 3. Add mapping to `mappings.json` ### Issue: "Invalid placement for channel" **Cause:** Mismatch between channel and placement **Solutions:** 1. Verify placement exists: `python get_dimensions.py` 2. Check channel structure (some channels need publisher+placement, others don't) 3. Update mapping with correct structure ### Issue: "Market not found" **Cause:** Country code mapping issue (usually GL = Global) **Solutions:** 1. Check if country exists: `python get_dimensions.py | grep -i country` 2. For "Global" uploads, ensure GL maps to market_name="Global" (no ISO code) 3. Update `country_mappings.SPECIAL_CASES` if needed --- ## Testing Mappings ### 1. Dry Run Test Test mapping without uploading: ```bash 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: ```bash 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 ```python 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_mappings` in `mappings.json` - Channel not mapped → Add to `channel_mappings` in `mappings.json` - Country not found → Check `country_mappings.SPECIAL_CASES` **Testing:** ```bash # 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`