- Modified _set_field_value to include 'type': 'string' in all code paths
- Adds type field when updating existing CreativeX URL field
- Ensures consistent structure whether creating or updating field
- Added 'type': 'string' to FERRERO.FIELD.CREATIVEX LINK value structure
- Fixes DAM validation error for CreativeX URL field
- Structure now matches DAM requirements
- Updated creativex_scoring_storing.py to map multiple placements to platforms
- Modified get_mapped_platform to get_mapped_platforms (returns list)
- Updated a2_to_a3_upload_polling.py to retrieve platforms list from DB
- Enhanced metadata_extractor_mvp.py to build multi-value CreativeX field
- Added DAM-CX mappings.csv for channel/placement to platform mapping
- Supports single channel with multiple placements generating multiple Platform^Score values
Simple, action-oriented guide focused on what users need to DO,
not how the system works internally. Perfect for onboarding and
daily reference.
USER_GUIDE.md (15-minute read):
Target Audience:
- Creative teams creating localized assets
- Agencies doing derivative work
- Campaign managers coordinating uploads
- Anyone who needs to USE the system (not maintain it)
Content Structure:
1. Big Picture (Simple Flowchart):
- 6-step process diagram
- "You do step 3-4, system handles rest"
- Clear role definition
2. 3 Golden Rules:
- Always use naming tool (never type manually)
- Every asset needs CreativeX score (no exceptions)
- Always use SAME tracking ID (for all versions)
3. Step-by-Step Workflow:
- Receive email → Download → Localize → Score → Name → Upload
- Each step explained in plain language
- What to look for in emails
- How to use naming tool (field-by-field)
- Where to upload
- What emails to expect
4. Rejection & Rework Process:
- What rejection means (normal, not failure)
- How to read rejection comments (Legal/IA&CC/Approver)
- How to fix and re-upload
- CRITICAL: Must re-score after fixes
- SAME tracking ID, NEW job number
5. Common Questions (10 FAQs):
- How to find tracking ID
- Do I really need 200 scores? (Yes!)
- What if typo in tracking ID?
- Can I upload before scoring? (Yes but not recommended)
- Wrong folder - what to do?
- How long to process? (5 minutes max)
- Can I edit filename? (NO!)
6. Troubleshooting:
- "File not processed" → Check folder, filename, tracking ID
- "Score=0 but I uploaded PDF" → Check filename match
- "Error: wrong tracking ID" → Copy from email exactly
7. Quick Checklist:
- 15-point checklist before upload
- 7 additional steps for rework
- All checkboxes format
8. What NOT to Do (5 critical don'ts):
- Don't type manually
- Don't skip CreativeX
- Don't reuse tracking IDs across campaigns
- Don't upload to wrong folder
- Don't edit generated filenames
9. Quick Reference Tables:
- Box folders and when to use
- Email types and meanings
- Naming tool field guide
- Contact information
Key Differences from Technical Guide:
❌ No system architecture
❌ No database schemas
❌ No Python code
❌ No technical troubleshooting
❌ No server commands
✅ What to click
✅ Where to upload
✅ How to use naming tool
✅ What emails mean
✅ How to fix common mistakes
✅ Who to contact
Tone:
- Friendly and supportive
- Clear and direct
- Action-oriented ("Do this, not that")
- Visual with tables and checklists
- Assumes no technical knowledge
Examples Are Real-World:
- Actual tracking IDs (pOiJ9s, a7K9mP)
- Actual folder IDs (348526703108)
- Real error messages users will see
- Common typos (pOlJ9s vs pOiJ9s)
Length: ~800 lines (~20 pages when formatted)
Perfect for:
- New agency onboarding
- Quick reference during work
- Sharing with non-technical stakeholders
- Training sessions
Complements COMPLETE_WORKFLOW_GUIDE.md (technical deep-dive)
with practical hands-on instructions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds master flowchart at document start showing complete workflow from
campaign creation through approval, including legal/compliance rejection
and rework cycles.
Master Workflow Diagram Features:
7 Phases Visualized:
1. Campaign Creation (A1 setup)
2. A1→A2 Master Download (automated every 5 min)
3. Agency Localization + CreativeX Scoring
4. A2→A3 Derivative Upload (automated)
5. Legal/Compliance/Brand Approval
6. A5→A6 Rejection Download (automated)
7. Agency Rework + Re-upload
Rejection Cycle Details:
- Legal reviewer adds compliance comments
- IA&CC reviewer adds brand guideline feedback
- General approver adds creative feedback
- All comments sent to agency in single email
- Agency fixes issues
- Re-scores with CreativeX (mandatory)
- Re-uploads with SAME tracking ID but NEW job number
- Re-enters A2→A3 flow (can repeat multiple times)
Color Coding:
🟣 Purple - CreativeX scoring (CRITICAL, highlighted twice)
🔵 Blue - Tracking IDs (critical links)
🔴 Red - Rejection path and comments
🟢 Green - Success/completion
🟠 Orange - Rework loop warnings
Critical Requirements Called Out:
1. "🔴 CRITICAL: Submit EVERY derivative to CreativeX"
- 200 derivatives = 200 analyses required
- Emphasized in agency phase
2. "🔴 Re-submit to CreativeX"
- MUST get new score for fixed version
- Emphasized in rework phase
3. Legal/IA&CC/Approver comment flow
- Shows 3 different reviewer types
- All feedback consolidated in email
4. Tracking ID reuse
- Blue highlighting shows where tracking IDs critical
- Same ID used throughout rework cycles
Example Shown:
- Original: 6666_NUT_SUMMER_OLV_30S_16x9_DE_de_pOiJ9s.mp4
- Rework: 7777_NUT_SUMMER_OLV_30S_16x9_DE_de_pOiJ9s.mp4
↑ New job number, SAME tracking ID ↑
Decision Points Visualized:
- All assets successful? (A1→A2)
- Score found in database? (A2→A3)
- Approved? (A3 review)
- Loops back if rejected
Placement:
- At very top of document (lines 11-136)
- Before Table of Contents
- First thing users see
- Sets context for entire guide
Impact:
Users immediately see complete workflow including rejection paths and
understand CreativeX is required at TWO points: initial upload AND rework.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Emphasizes mandatory requirement that EVERY derivative asset must be
scored individually - master scores do not apply to derivatives.
New Section Added (1,830-2,055):
"🔴 CRITICAL REQUIREMENT: Every Derivative Needs Its Own CreativeX Score"
Key Points Emphasized:
The Rule:
- 1 master → 10 derivatives = 10 scores required
- 1 master → 200 derivatives = 200 scores required
- NO EXCEPTIONS for client deliverables
Why Every Derivative Needs Scoring:
1. Each localization is different execution (voice-over, subtitles, pacing)
2. Master score is reference only, NOT applicable to derivatives
3. Scores are asset-specific, not campaign-specific
4. Reporting requires individual scores for market analysis
The Math Example:
- 1 master asset
- 20 markets × 2 languages = 40 derivatives
- 3 aspect ratios = 120 versions
- 2 platforms = 240 total derivatives
- REQUIRED: 240 CreativeX PDF reports
What Happens Without Scoring:
- Assets upload with Score=0 (default)
- Looks unprofessional (zero implies poor quality)
- Incomplete reporting (can't analyze market performance)
- Inconsistent quality control
- Client questions raised
Correct Workflow (Mermaid Sequence Diagram):
- Create derivative
- Submit to CreativeX
- Get PDF report
- Upload PDF to Box 350605024645
- Run scoring script
- THEN upload derivative to A2→A3
- Score automatically attached
Batch Processing Strategy:
- Week-by-week approach for 100+ derivatives
- Create 25 → Score 25 → Upload 25 (repeat)
- NOT: Create all → Upload all → Score later (too late)
Common Mistake Addressed:
"I'll only score the important ones" → Why this fails:
- Client contracts may require all scored
- Reporting incomplete
- Selective scoring = inconsistent quality
- Score=0 is visible and looks bad
Verification Methods:
- Count derivatives vs scores in database (should match)
- Check logs for "CreativeX Score Missing" warnings
- Query database before and after uploads
Exceptions Where Score=0 Acceptable:
- Internal reference assets
- Template files
- Work-in-progress
- Non-creative assets (documents, spreadsheets)
Impact:
Users now understand CreativeX scoring is NOT optional. Every asset
created requires individual analysis and scoring. Plan projects with
this requirement from the start.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enables asset type to be updated from derivative filename and refactors
_update_fields() to use filename_updates configuration dynamically.
Field Mappings Configuration (field_mappings.yaml):
Added to filename_updates:
- FERRERO.FIELD.MKTG.ASSET TYPE:
source: asset_type
required: true
Now updates from derivative filename:
- ROC_TEST-E2E2_EHI_1x1_DE_de.png → Asset Type = "EHI"
Metadata Extractor Refactor (metadata_extractor_mvp.py):
Old _update_fields():
- Hardcoded field updates (ASSET NAME, DESCRIPTION, STATE)
- Not using filename_updates configuration
- Required code changes to add new fields
New _update_fields():
- Dynamically processes filename_updates from config
- Supports transform: uppercase/lowercase
- Supports any source field from parsed_filename
- Uses forced_values from config (was hardcoded before)
- Add new fields via config, no code changes needed
Configuration-Driven Updates:
- ARTESIA.FIELD.ASSET NAME ← clean_filename
- ARTESIA.FIELD.ASSET DESCRIPTION ← subject_title
- FERRERO.FIELD.MKTG.ASSET TYPE ← asset_type (NEW)
- MAIN_LANGUAGES ← language_code (uppercase)
- FERRERO.FIELD.STATE ← "Local" (forced value)
Benefits:
- Asset type now correctly populated from filename
- Configuration-driven (add fields without code changes)
- Cleaner code (uses config instead of hardcoded logic)
- Forced values also configurable
- Easier to maintain and extend
Example:
Filename: ROC_TEST-E2E2_EHI_1x1_DE_de.png
Parsed asset_type: "EHI"
Field FERRERO.FIELD.MKTG.ASSET TYPE updated to: "EHI"
Impact:
All A2→A3 uploads will now have correct Asset Type from derivative
filename instead of inheriting from master (which may be different).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Documents database migration for tracking_id column and explains
master-cx-score status for scores extracted from A1→A2 master assets.
Documentation Updates:
Database Table Creation:
- Added tracking_id VARCHAR(6) to CREATE TABLE statement
- Added idx_creativex_tracking_id index
- Included migration SQL for existing databases (ALTER TABLE)
Status Values Documented:
- 'active' - Current derivative score (PDF extraction)
- 'superseded' - Old derivative scores (version history)
- 'master-cx-score' - Master asset score (A1→A2, reference only)
Workflow Section Split:
- CreativeX PDF Extraction (manual process)
- Master Asset CreativeX (automatic during A1→A2)
- Clarifies master scores NOT used for uploads
New Query Examples:
- Get master score by tracking_id
- View all master scores
- Count records by status (shows master_scores separately)
- Updated history queries to include tracking_id column
Migration Instructions:
Production servers with existing creativex_scores table should run:
ALTER TABLE creativex_scores ADD COLUMN tracking_id VARCHAR(6);
CREATE INDEX idx_creativex_tracking_id ON creativex_scores(tracking_id);
Use Case Clarification:
Master scores stored for reference/reporting/analytics only.
A2→A3 uploads always use filename-based lookup (derivative scores).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Stores master asset CreativeX scores from DAM metadata during A1→A2
download for reference/reporting purposes (not used in uploads).
Database Changes:
creativex_scores table:
- Added: tracking_id VARCHAR(6) column
- Added: idx_creativex_tracking_id index
- Updated comment: status can be 'active', 'superseded', or 'master-cx-score'
Status Values:
- 'active' - Current derivative score (from PDF extraction)
- 'superseded' - Old derivative score (version history)
- 'master-cx-score' - Master asset score (from A1→A2 DAM metadata) ← NEW
Migration SQL (for existing databases):
ALTER TABLE creativex_scores ADD COLUMN tracking_id VARCHAR(6);
CREATE INDEX idx_creativex_tracking_id ON creativex_scores(tracking_id);
Database Method Updates (database.py):
store_creativex_score() signature:
- Added: tracking_id parameter (optional, default None)
- Added: status parameter (optional, default 'active')
Logic:
- If status='master-cx-score': Simple insert, no versioning
- If status='active': Soft delete versioning as before
- Always stores tracking_id if provided
A1→A2 Script Updates (a1_to_a2_download.py):
New Function: extract_creativex_from_dam_metadata()
- Searches metadata_element_list for CREATIVEX fields
- Extracts FERRERO.TAB.FIELD.CREATIVEX (score)
- Extracts FERRERO.FIELD.CREATIVEX LINK (url)
- Returns dict with score/url or None if not found
- Handles tabular field structure for score
- Handles nested value structure for URL
Integration:
- After successful master asset storage
- Extracts CreativeX from asset metadata
- If found: Stores in creativex_scores with status='master-cx-score'
- Links to master via tracking_id
- Logs when score found/stored
- Logs "normal" when not found (not all masters are scored)
Use Cases:
A2→A3 Upload:
- Still uses filename-based lookup ONLY ✅
- No changes to A2→A3 logic ✅
- Master scores not used for uploads ✅
Reporting/Analytics Tools:
- Can query master score by tracking_id
- Compare master vs derivative scores
- Track score improvements
- Audit trail
Query Examples:
-- Get master score for tracking ID
SELECT * FROM creativex_scores
WHERE tracking_id = '7xXgKp' AND status = 'master-cx-score';
-- Get derivative score for filename
SELECT * FROM creativex_scores
WHERE filename = 'file.mp4' AND status = 'active';
Test Record Created:
- Filename: nutella_pbased.jpg
- Tracking ID: 7xXgKp
- Score: 85
- Status: master-cx-score
Benefits:
- Historical reference of master scores
- Enables score comparison analytics
- No impact on A2→A3 upload logic
- Automatic extraction during A1→A2
- Optional (works even if masters don't have scores)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Complete rewrite of filename parser to support new field order where
Subject/Asset moved up and Country/Language moved down, plus new
Social Media field.
BREAKING CHANGE: V2.1 Structure (November 2025)
Old (V1):
[JOB]_[BRAND]_[COUNTRY]_[LANG]_[SUBJECT]_[ASSET]_[SPOT]_[DUR]_[RATIO]_[TRACKING]
New (V2.1):
[JOB]_[BRAND]_[SUBJECT]_[ASSET]_[DUR]_[RATIO]_[SPOT]_[COUNTRY]_[LANG]_[SOCIAL]_[TRACKING]
Field Position Changes:
- Subject Title: Position 5 → 3 (MOVED UP)
- Asset Type: Position 6 → 4 (MOVED UP)
- Duration: Position 8 → 5 (MOVED UP)
- Aspect Ratio: Position 9 → 6 (MOVED UP)
- Spot Version: 7 → 7 (SAME)
- Country Code: Position 3 → 8 (MOVED DOWN)
- Language Code: Position 4 → 9 (MOVED DOWN)
- Social Media: NEW → Position 10
- Tracking ID: Position 10 → 11
New Social Media Field:
- Field: social_media_version
- Position: 10 (after language, before tracking)
- Format: 3 uppercase letters
- Codes: FBP, FBR, IGF, IGR (expandable)
- Optional: Only present for social media assets
Parse Algorithm Changes:
- Positions 1-4 now: Job, Brand, Subject, Asset (fixed)
- Positions 5-11: Pattern-based detection (flexible)
- Duration detected by \d+S pattern
- Aspect ratio detected by \d+x\d+ or contains 'x'
- Spot detected by MST/REF
- Country detected by 2 upper alpha (after ratio)
- Language detected by 2-3 lower alpha (after country)
- Social detected by known codes (after language)
- Tracking ID detected by 6 alphanumeric + optional -N
strip_upload_components() Updated:
Now outputs: [BRAND]_[SUBJECT]_[ASSET]_[DUR]_[RATIO]_[SPOT]_[COUNTRY]_[LANG]_[SOCIAL]
- Includes social media version if present
- Still strips job number and tracking ID
Testing:
All 7 test cases from specification passed:
✅ All fields present
✅ Minimal (no duration/social/tracking)
✅ No duration
✅ No spot version
✅ With -N tracking (folder-only mode)
✅ No social media (most common)
✅ No tracking ID
Example:
Input: 1234567_RAF_TEST_OLV_6S_1x1_REF_GL_it_IGF_abc123.mp4
Parsed: brand=RAF, subject=TEST, country=GL, lang=it, social=IGF
Clean: RAF_TEST_OLV_6S_1x1_REF_GL_it_IGF.mp4
Backward Compatibility:
None - system not live yet, clean cutover to V2.1 format only.
Backup: filename_parser_v1_backup.py contains old version for reference.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes issue where CreativeX score field was not appearing in final upload
because it didn't exist in the master metadata from DAM.
Problem:
- Master metadata from A1→A2 doesn't include CREATIVEX fields (new fields)
- _update_creativex_fields() only UPDATED existing fields
- If field not present, it logged error but didn't add the field
- Result: CREATIVEX score missing from upload, only URL appeared
Solution:
- Check if CREATIVEX Score field exists in mvp_fields
- If NOT found: Create and append field with proper structure
- If found: Update value as before
- Same logic for CREATIVEX URL field
Field Structures Created:
CREATIVEX Score (FERRERO.TAB.FIELD.CREATIVEX):
- Type: MetadataTableField (tabular field)
- Parent: FERRERO.TABULAR.FIELD.PLATFORMRATING
- Data type: INTEGER
- Value structure: {'value': {'value': score}}
CREATIVEX URL (FERRERO.FIELD.CREATIVEX LINK):
- Type: MetadataField (regular field)
- Data type: CHAR
- Value structure: {'value': {'value': url}}
Logging:
- Changed from ERROR to WARNING when field not found
- Logs "adding it now" instead of just error
- Confirms field added with value
Impact:
Both CreativeX fields will now appear in uploads even if master
metadata doesn't have them (common for older campaigns downloaded
before CreativeX integration).
Testing:
Run with --dryrun to verify both CREATIVEX fields in JSON output.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changes database lookup strategy to match on full filename as it appears
in Box and in the CreativeX PDF report filename field.
Critical Design Change:
Old (incorrect):
- Strip job number and tracking ID from Box filename
- Lookup: NUT_PL_pl_TEST-E2E_EHI_1x1.png
- Database has: 6487512_NUT_PL_pl_TEST-E2E_EHI_1x1_7xXgKp.png
- RESULT: No match found, uses defaults
New (correct):
- Use original Box filename for lookup
- Lookup: 6487512_NUT_PL_pl_TEST-E2E_EHI_1x1_7xXgKp.png
- Database has: 6487512_NUT_PL_pl_TEST-E2E_EHI_1x1_7xXgKp.png
- RESULT: Match found, uses actual score
Rationale:
The CreativeX PDF report contains a "filename" field that stores the
actual asset filename including job number and tracking ID. This is
the name that gets extracted by LlamaExtract and stored in database.
The A2→A3 workflow receives files from Box with the SAME filename
structure (job_brand_country_lang_subject_trackingID.ext).
Therefore, we match on the complete original filename, not the stripped
version.
Database Storage Pattern:
- CreativeX PDF named: anything.pdf (name doesn't matter)
- PDF contains field: filename = "6487512_NUT_PL_pl_TEST-E2E_EHI_1x1_7xXgKp.png"
- Database stores: filename = "6487512_NUT_PL_pl_TEST-E2E_EHI_1x1_7xXgKp.png"
- A2→A3 receives: 6487512_NUT_PL_pl_TEST-E2E_EHI_1x1_7xXgKp.png from Box
- Lookup matches exactly
Clean filename still used for DAM upload, only the lookup is on original.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds detailed logging to trace exactly how field values are being set
and diagnose why CreativeX score/URL aren't appearing in final JSON.
Changes to _set_field_value():
- Logs field ID being updated
- Logs current field['value'] structure BEFORE setting
- Logs which code path is taken (nested vs created)
- Logs field['value'] structure AFTER setting
- Shows full JSON structure at each step
Output Example:
_set_field_value called for: FERRERO.TAB.FIELD.CREATIVEX with value: 85
Current field['value']: {
"is_locked": false,
"domain_value": false,
...
}
Created field['value'] = {'value': {'value': 85}}
After setting, field['value']: {
"value": {
"value": 85
}
}
Purpose:
Diagnose why CreativeX fields show empty value dicts in asset
representation even though logs say "Set CREATIVEX Score to: 0".
This verbose logging will show:
1. What the field structure looks like before we set it
2. Which code path is executed
3. What the field structure looks like after we set it
4. Whether the value is actually being placed in the right location
Run with --dryrun to see full debug output without uploading.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>