Commit graph

63 commits

Author SHA1 Message Date
DJP
00f35169a1 Add Global campaign search methods and update debug to show both Local and Global
StatusManager Enhancements:
 searchGlobalCampaigns() - Search for 'Global comm' campaigns (B1-B2 workflow)
 searchAllCampaignTypes() - Search for ALL campaigns (Local + Global)

Workflow Updates:
 Debug search now uses searchAllCampaignTypes()
 Shows both 'Local Adaptation' and 'Global comm' campaigns
 Updated success message: 'Local Adaptation + Global comm'

This fixes the issue where B1 campaigns weren't showing in debug view.

Test:
1. Click '🔍 Debug: Load ALL Campaigns' in any workflow tab
2. Should now see Global comm campaigns with B1/B2 status
3. Example: NUTELLA PLANT-BASED LAUNCH (Status: B1)

Next: Add dedicated B1→B2 workflow tab and status change buttons

🤖 Generated with Claude Code
2025-11-03 12:25:28 -05:00
DJP
b273fdafee Fix CREATIVEX extraction - detect at category level not field level
Issue: CREATIVEX fields still not appearing
Root Cause: FERRERO.FIELD.CREATIX is a CATEGORY, not a field within a category

Fix:
- Check category ID/name for 'CREATIX' or 'CreativeX'
- When CREATIVEX category found, extract ALL items within it
- Handle both tables and direct fields in CREATIVEX category
- Show fields even if empty (displays structure)

Structure:
Category: FERRERO.FIELD.CREATIX (name: CreativeX)
  ├─ Table: FERRERO.TABULAR.FIELD.CREATIVEX (Confidence)
  │   └─ Field: FERRERO.TAB.FIELD.CREATIVEX (Platform > Rating %)
  └─ Field: FERRERO.FIELD.CREATIVEX LINK (CreativeX Hyperlink)

Test Results:
 Extracted 2 CREATIVEX fields
 Platform > Rating (%): (empty)
 CreativeX Hyperlink: (empty)

Now purple CREATIVEX section will appear in metadata viewer!

🤖 Generated with Claude Code
2025-10-31 11:29:06 -04:00
DJP
70d4f1be71 Fix CREATIVEX field extraction - search for CREATIX and handle nested structure
Issue: CREATIVEX fields not showing despite being in metadata
Root Cause: Parent field is FERRERO.FIELD.CREATIX (not CREATIVEX)

Fixes:
1. Search for both 'CREATIX' and 'CREATIVEX' in field IDs
2. Handle deeply nested structure:
   - Parent: FERRERO.FIELD.CREATIX (name: CreativeX)
   - Contains: FERRERO.TABULAR.FIELD.CREATIVEX (tables)
   - Contains: FERRERO.TAB.FIELD.CREATIVEX (actual fields)
   - Plus: FERRERO.FIELD.CREATIVEX LINK (direct field)

Enhanced Extraction:
- Detects parent CREATIX fields
- Recursively extracts from nested metadata_element_list
- Handles nested tables within tables
- Extracts values from nested fields
- Handles both tabular and direct CREATIVEX fields

Field Structure:
FERRERO.FIELD.CREATIX (parent)
  └─ FERRERO.TABULAR.FIELD.CREATIVEX (table)
     └─ FERRERO.TAB.FIELD.CREATIVEX (fields with values)

Now CREATIVEX fields should appear in purple section when viewing metadata.

🤖 Generated with Claude Code
2025-10-31 10:14:24 -04:00
DJP
c35d027724 Add dedicated CREATIVEX fields section to metadata viewer
Added CREATIVEX Field Display:
 New dedicated section in metadata viewer
 Purple border-left (color: #9b59b6) for visibility
 Shows count in summary: 🎯 CREATIVEX Fields (X)
 Grid layout: Field Name | Value
 Shows field ID below value for reference
 Auto-expanded (open attribute)
 Applied to both Master Assets and Final Assets views

MetadataExtractor Enhancement:
 Extracts all CREATIVEX fields separately
 Stores in metadata['creativex_fields']
 Handles both regular and tabular CREATIVEX fields
 Handles multiple value structures

Display Features:
- Field name as label
- Value displayed prominently
- Field ID shown below (gray text)
- Supports array values (comma-separated)
- Easy to identify with purple color
- Section expanded by default for visibility

CREATIVEX fields will now be prominently displayed in their own
section when viewing Master Assets or Final Assets metadata.

🤖 Generated with Claude Code
2025-10-31 10:05:30 -04:00
DJP
4e1a7fa588 Add video dimensions to upload and disable CREATOR field
Changes:
1. Default Box Folder ID
   - Pre-filled with: 348526703108
   - Saves time entering same folder repeatedly
   - Still editable for different folders

2. Video Dimensions in Upload Manifest
   - Extract video metadata via ffprobe (width, height, duration, bitrate)
   - Add width and height to upload manifest
   - Manifest now includes: file_name, file_type, width, height
   - Should fix Content Info showing -1 for Width/Height

3. CREATOR Field Disabled
   - "ExternalAgency" value still causes upload failure
   - Leaving CREATOR field from master unchanged
   - Need to investigate exact field structure

4. Working Field Updates (Re-enabled):
    ARTESIA.FIELD.ASSET DESCRIPTION → subject_title from filename
    FERRERO.FIELD.STATE → "Local"
    MAIN_LANGUAGES → language_code from filename

Upload Flow Now:
1. Parse V2 filename
2. Load master metadata from DB
3. Download file from Box
4. Extract video metadata (ffprobe)
5. Build MVP asset rep with field updates
6. Add video dimensions to upload manifest
7. Upload to DAM
8. Width/Height should populate (not -1)

Logging:
- "VideoMetadata: Extracted - 1024x576, 2s, 1006892 bps"
- "SIMPLE UPLOADER: Adding video dimensions to manifest - 1024x576"

🤖 Generated with Claude Code
2025-10-30 15:50:36 -04:00
DJP
ad4351920b Enable ExternalAgency for CREATOR field
Re-enabled FERRERO.MARKETING.CREATOR update:
- Sets to 'ExternalAgency' (valid domain value)
- Updates existing field if present in master
- Adds field if not present in master
- Validated against DAM_LOOKUPDOMAINS_RAW.json

Field Updates Now Active:
 Description → From filename
 State → Local
 Language → From filename
 Creator → ExternalAgency

Note on Width/Height (-1):
- DAM may analyze asynchronously after upload
- If still -1 after waiting, we can send in upload manifest
- Video metadata is being extracted via ffprobe (logged)

🤖 Generated with Claude Code
2025-10-30 15:45:40 -04:00
DJP
f2fcaf65ef Add default Box folder ID and re-enable working field updates
UI Changes:
- Box Folder ID input now defaults to: 348526703108
- Pre-filled but still editable
- Saves time re-entering same folder

Field Updates RE-ENABLED (confirmed working):
1. ARTESIA.FIELD.ASSET DESCRIPTION
   - Set to subject_title from V2 filename
   - Example: "TEST-JOB11"

2. FERRERO.FIELD.STATE
   - Force to "Local" for all uploads
   - Valid domain value (Global/Local)

3. MAIN_LANGUAGES
   - Set from V2 filename language_code
   - Example: "de" → "DE"

Field Updates KEPT DISABLED:
- FERRERO.MARKETING.CREATOR
  - "Oliver Agency" is invalid (not in 1,893 domain values)
  - Keeping master value to avoid validation failure
  - Valid options: "ExternalAgency", "ExternalAgencynew"

Video Metadata:
- Extracted via ffprobe (width, height, duration, bitrate)
- Logged for reference
- Not sent to DAM (DAM analyzes automatically)

Result:
 Default folder ID saves time
 Description from filename
 State always Local
 Language from filename
 All uploads should work

🤖 Generated with Claude Code
2025-10-30 15:42:57 -04:00
DJP
dfaa1fe4de Temporarily disable field updates - Testing which updates cause upload failure
Issue: After adding field updates, uploads return HTTP 202 but assets don't appear

Field Updates DISABLED for testing:
1. ARTESIA.FIELD.ASSET DESCRIPTION (from filename)
2. FERRERO.FIELD.STATE (force to Local)
3. FERRERO.MARKETING.CREATOR (Oliver Agency)

Findings from lookup domains:
 FERRERO.FIELD.STATE domain values: Global, Local (valid)
 FERRERO.MARKETING.CREATOR: 1,893 specific values (emails/usernames)
   - 'Oliver Agency' NOT in list
   - Valid options: 'ExternalAgency', 'ExternalAgencynew'

Video metadata:
- Still extracted via ffprobe
- Only logged, not sent to DAM
- Let DAM analyze video files automatically

Next Step:
Test upload with field updates disabled.
If works → Re-enable one at a time to find the breaking change.

🤖 Generated with Claude Code
2025-10-30 15:36:49 -04:00
DJP
5b050c2483 Add field updates from V2 filename and video file analysis
New Features:
1. VideoMetadataExtractor class
   - Uses ffprobe to extract video technical metadata
   - Width, height, duration, bitrate, aspect ratio
   - Frame rate, codec info
   - Formatted duration (HH:MM:SS:FF timecode)
   - Formatted aspect ratio (16:9, 4:3, etc.)

2. Updated MetadataExtractorMVP
   - Now accepts local file path parameter
   - Extracts video metadata from downloaded file
   - Updates fields from V2 filename:
     * ARTESIA.FIELD.ASSET DESCRIPTION ← subject_title from filename
     * FERRERO.FIELD.STATE ← Always "Local" for uploads
     * FERRERO.FIELD.MKTG.ASSET TYPE ← asset_type from filename
     * MAIN_LANGUAGES ← language_code from filename
   - Adds FERRERO.MARKETING.CREATOR ← "Oliver Agency"
   - Logs video dimensions and technical data

3. Workflow integration
   - Pass local file path to buildMVPAssetRepresentation()
   - Video analysis happens after Box download
   - File metadata extracted before upload

Field Updates Summary:
 Description → From filename (TEST-JOB11)
 State → Local (forced)
 Creator → Oliver Agency
 Main Languages → From filename (de → DE)
 Asset Type → From filename (OLV)
 Video metadata extracted (width, height, duration, bitrate)

Next: Test ffprobe extraction and determine where to inject video
technical fields in upload structure (content_info vs metadata).

🤖 Generated with Claude Code
2025-10-30 14:57:54 -04:00
DJP
b31664d88d Stage 2 Complete: Add missing MVP fields from V2 filename + Asset Rep JSON viewer
UI Improvements:
1. Fixed Final Assets metadata display format
   - Now matches Master Assets format (field names, not IDs)
   - Grid layout: "Asset Type: TVC" instead of "FERRERO.FIELD.MKTG.ASSET TYPE\nValue: TVC"
   - Collapsed/expandable categories
   - Same visual style as Master Assets

2. Added Asset Representation JSON viewer
   - 💾 Download button for each successful upload
   - 👁️ View button to see JSON in modal
   - Download saves as: asset_representation_{asset_id}.json
   - Modal view shows formatted JSON with syntax highlighting
   - Click outside modal to close

Stage 2 - V2 Filename Integration:
1. MetadataExtractorMVP.addMissingFieldsFromFilename()
   - Adds MAIN_LANGUAGES from V2 filename language_code (de → DE)
   - Updates FERRERO.FIELD.MKTG.ASSET TYPE from filename asset_type
   - Adds FERRERO.FIELD.ASSETCOMPLIANCE (default: -)
   - Adds MARKETING_TAG (default: Tag)

2. buildMVPAssetRepresentation() enhanced
   - Now accepts parsed V2 filename data
   - Extracts MVP fields from master (24 fields)
   - Adds missing fields from filename (up to 4 more)
   - Updates ASSET NAME to clean filename
   - Total: Up to 28 MVP fields

Logging:
- "Adding MAIN_LANGUAGES from filename: DE"
- "Updated ASSET TYPE from filename: olv"
- "Adding FERRERO.FIELD.ASSETCOMPLIANCE with default: -"
- Shows field count: "Found X out of 28 MVP fields"

Result:
 MVP fields extracted from master
 Missing fields filled from V2 filename
 Asset representation viewable/downloadable
 Metadata display matches Master Assets format

🤖 Generated with Claude Code
2025-10-30 14:28:12 -04:00
DJP
ed3b37ae71 Add debug logging to MVP extractor to diagnose metadata structure
Added comprehensive logging to track:
- What keys are in the received metadata
- Which path is being used (1, 2, or 3 levels deep)
- How many categories/fields found
- Keys available if metadata_element_list not found

This will help us identify the correct path to the metadata structure.

Expected paths:
- 3 levels: masterAsset['metadata']['metadata']['metadata_element_list']
- 2 levels: masterAsset['metadata']['metadata_element_list']
- 1 level: masterAsset['metadata_element_list']

🤖 Generated with Claude Code
2025-10-30 13:39:06 -04:00
DJP
811cff6fc6 Implement MVP metadata extraction - Extract specific fields from master
New Approach:
Instead of sending ALL metadata or just 5 fields, extract ONLY the
MVP fields from the master asset metadata.

MetadataExtractorMVP.php:
- New class dedicated to MVP field extraction
- Lists all 28 MVP field IDs from asset_representation MVP.json
- extractMVPFields(): Searches master metadata for MVP fields
- Preserves exact field structure from master (domain values, tabular fields)
- buildMVPAssetRepresentation(): Creates upload JSON with only MVP fields
- Updates ASSET NAME field to clean filename

MVP Fields Extracted (28 total):
- FERRERO.FIELD.MKTG.ASSET TYPE
- FERRERO.FIELD.FISCAL YEAR
- MAIN_LANGUAGES
- FERRERO.FIELD.ASSETCOMPLIANCE
- MARKETING_TAG
- FERRERO.MARKET.FIELD.TYPE_VID
- ARTESIA.FIELD.ASSET DESCRIPTION
- FERRERO.FIELD.MARKETING.FLAVOUR
- FERRERO.FIELD.MARKETING.SIZE
- FERRERO.FIELD.STATE
- ARTESIA.FIELD.ASSET NAME
- FERRERO.FIELD.SUB BRAND
- FERRERO.FIELD.ASSET VALIDITY START/END PERIOD
- FERRERO.MARKETING.FIELD.AGENCY NAME
- FERRERO.MARKET.FIELD.IPRIGHT
- FERRERO.MARKET.PROD_COMPANY
- [... and 12 more fields]

Workflow Changes:
- Use MetadataExtractorMVP instead of MetadataMerger for building asset rep
- Extract MVP fields from full master metadata
- Update ASSET NAME to clean filename
- Log how many fields found vs expected

Benefits:
 Includes all MVP metadata (not just 5 fields)
 Preserves exact field structures from master
 Smaller payload than full metadata (~10-15KB vs 128KB)
 Should work reliably while including more metadata

Next: Test if uploads now appear in folder with MVP metadata.

🤖 Generated with Claude Code
2025-10-30 13:05:09 -04:00
DJP
0ee447e1dc Rollback to simple 5-field metadata structure for uploads
Issue: Assets reporting HTTP 202 success but not appearing in folder
Hypothesis: Large metadata payload (128KB) may be causing silent failures

Rollback Changes:
- MetadataMerger.buildAssetRepresentation() now creates SIMPLE structure
- Only 5 fields (proven to work):
  1. FERRERO.FIELD.MKTG.ASSET TYPE (from filename)
  2. FERRERO.FIELD.FISCAL YEAR (default)
  3. MAIN_LANGUAGES (from filename)
  4. ARTESIA.FIELD.ASSET NAME (clean filename)
  5. FERRERO.FIELD.STATE (Local)

- Removed complex master metadata merging
- Added logging for field values
- Pass parsed filename to buildAssetRepresentation()

Test this to see if uploads now appear in Final Assets folder.

Note on DELETE:
HTTP 405 - Method not supported. DAM may not allow DELETE via API.
Need to investigate alternative approach (move to trash, mark as deleted, etc.)

🤖 Generated with Claude Code
2025-10-29 17:46:07 -04:00
DJP
143b29db65 Add detailed logging to AssetUploaderSimple for debugging
Added error_log statements to track:
- HTTP response codes
- Success/failure status
- Asset IDs on success
- Error messages on failure
- Response body preview on failure

This will help diagnose upload issues.

🤖 Generated with Claude Code
2025-10-29 17:26:41 -04:00
DJP
8ab9ccf9c9 Rewrite MetadataMerger to preserve ALL master metadata fields
Major Changes:
- Now loads COMPLETE master metadata from database
- Preserves ALL fields from master asset (16+ fields from MVP)
- Only overrides 3 specific fields from filename:
  1. ARTESIA.FIELD.ASSET NAME (clean filename)
  2. MAIN_LANGUAGES (language from V2 filename)
  3. FERRERO.FIELD.MKTG.ASSET TYPE (asset type from filename)

Technical Implementation:
- Added updateFieldInCategorizedStructure() method
- Handles DAM's nested category structure properly
- Searches through all categories to find and update fields
- Preserves original field structure and formatting
- Supports both regular and tabular fields
- Logs which fields are updated

Metadata Flow:
BEFORE: Created only 5-6 fields (hardcoded)
AFTER: Takes ALL master fields + overrides 3 from filename

Example:
Master has 16+ fields → All preserved
Filename: RAF_DE_de → Overrides language to "DE", asset name to clean filename
Result: 16+ fields uploaded with 3 updated from filename

This ensures all master metadata is maintained while allowing filename-based updates.

🤖 Generated with Claude Code
2025-10-29 17:07:49 -04:00
DJP
2bcac0a08f Add full_metadata JSONB column - Store complete DAM metadata without truncation
Database Changes:
- ALTER TABLE master_assets ADD COLUMN full_metadata JSONB
- Stores COMPLETE DAM asset metadata (no 5,000 char truncation)
- PostgreSQL JSONB type for efficient storage and querying

DatabaseClient Changes:
- Added full_metadata to INSERT and ON CONFLICT UPDATE
- Store complete json_encode($assetData) in full_metadata column
- Simplified description to just Box info (no metadata)
- Log metadata size when storing
- NO TRUNCATION - preserves all fields

Workflow Changes (workflow_v3.php):
- load_master_metadata: Read from full_metadata JSONB column
- upload_from_box: Read from full_metadata JSONB column
- Both endpoints now get COMPLETE master metadata
- Added logging for metadata size verification

Impact:
BEFORE: Only 5,000 chars stored (truncated)
AFTER: Full metadata stored (10,000+ chars, all fields preserved)

Next Step:
Re-download master assets to populate full_metadata for existing records.
New downloads will automatically use the new column.

🤖 Generated with Claude Code
2025-10-29 16:53:51 -04:00
DJP
8e627708ce Fix asset name to use clean filename without OMG Job and Tracking ID
Issue: Assets uploaded with original filename including OMG Job # and Tracking ID
Fix: MetadataMerger now builds clean filename from parsed components for ASSET NAME field

Changes:
1. MetadataMerger.applyFilenameData()
   - Build clean filename from components (no OMG Job, no Tracking ID)
   - Use for ARTESIA.FIELD.ASSET NAME metadata
   - Format: BRAND_COUNTRY_LANG_TITLE_TYPE_VER_SECS_RATIO

2. debug_assets.php
   - Show "Get Assets" button for ALL folders (not just Master Assets)
   - Allows viewing Final Assets folder contents
   - Added campaign_id parameter to maintain context

Result:
Before: 1234567_RAF_DE_de_TEST-JOB_OLV_001_6S_16x9_BJP4ho.mp4
After:  RAF_DE_de_TEST-JOB_OLV_001_6S_16x9.mp4

Both the file AND the metadata now use the clean filename.

🤖 Generated with Claude Code
2025-10-29 16:36:04 -04:00
DJP
bd9030c0ab Fix upload processing - use correct method and metadata extraction
Changes:
1. Added uploadWithMetadata() method to AssetUploaderSimple
   - Accepts custom asset representation from MetadataMerger
   - Same proven structure as uploadFile() but with custom metadata

2. Fixed database metadata extraction in upload_from_box
   - Metadata stored in description field, not separate column
   - Extract JSON after 'DAM Metadata JSON:' marker
   - Parse and store in masterAsset['metadata']

3. Updated upload call to use uploadWithMetadata()
   - Rename temp file to clean filename before upload
   - Pass merged asset representation
   - Upload to correct folder

This fixes all the upload processing errors.

🤖 Generated with Claude Code
2025-10-29 16:25:38 -04:00
DJP
4e3f47206d Add getConnection() method to DatabaseClient
DatabaseClient now exposes the PDO connection via getConnection().
This allows the upload_from_box AJAX endpoint to query the database.

🤖 Generated with Claude Code
2025-10-29 16:20:43 -04:00
DJP
d3f3063eaf Fix Box integration issues
Added missing getAccessToken() method to BoxClient
Fixed BoxFileRetriever to properly initialize BoxClient with credentials from config

Changes:
- BoxClient: Added public getAccessToken() method
- BoxFileRetriever: Load Box config and extract clientID/clientSecret
- BoxFileRetriever: Pass credentials to BoxClient constructor

This fixes the 'Call to undefined method' error when loading Box files.

🤖 Generated with Claude Code
2025-10-29 16:12:22 -04:00
DJP
fac5707451 Fix: Remove space from method name in BoxFileRetriever
Fatal error fix: listFilesWithTracking IDs → listFilesWithTrackingIDs
This was preventing workflow_v3.php from loading.

🤖 Generated with Claude Code
2025-10-29 16:04:27 -04:00
DJP
3a95076726 Add Upload from Box workflow - Phase 1 Complete
Core Components Implemented:
- FilenameParser: V2 naming convention parser with strict validation
- MetadataMerger: Merge master + filename metadata (filename priority)
- BoxFileRetriever: List/download files from Box folders
- DAM Lookup Domains: Complete documentation (182 domains)

Features:
- Parse V2 filenames: OMG_JOB_BRAND_COUNTRY_LANG_TITLE_TYPE_VER_SEC_RATIO_TRACKING
- Strip upload components (Job Number & Tracking ID)
- Extract tracking IDs and load master metadata from PostgreSQL
- Merge metadata with filename always winning
- Identify editable vs locked fields
- Build proper asset representation for DAM upload

Files Added:
- src/FilenameParser.php (tested - 8/8 passing)
- src/MetadataMerger.php
- src/BoxFileRetriever.php
- ECOMMERCE_ALLOWED_FIELDS.md (182 lookup domains)
- DAM_LOOKUPDOMAINS_RAW.json (15MB raw data)
- test_filename_parser.php
- fetch_lookupdomains.php
- UPLOAD_FROM_BOX_STATUS.md (complete documentation)

Next Phase: UI integration - Add "Upload from Box" tab to workflow_v3.php

🤖 Generated with Claude Code
2025-10-29 15:47:30 -04:00
DJP
53b724d5fd Add detailed logging inside getFieldValue to debug NULL returns
Will show:
- What value structure is found
- Which path is used (domain vs regular)
- Why fields return NULL

This will reveal the actual value structure for SUB BRAND, MAIN LANGUAGES, etc.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 14:42:11 -04:00
DJP
4665e58b03 Clean up metadata extraction - focus on asset-level fields
Removed:
- CAMPAIGN_BRAND lookup (doesn't exist in assets)
- CAMPAIGN_MARKET lookup (doesn't exist in assets)

Improved:
- SUB BRAND extraction
- MAIN LANGUAGES tabular field handling
- MKTG.ASSET TYPE extraction

These fields actually exist in the asset metadata.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 14:34:53 -04:00
DJP
fd3ed388e7 Extract metadata from ASSET fields (not campaign fields)
Fixed metadata extraction to use fields that exist in assets:
- SUB BRAND → brand_name, brand_code
- MAIN LANGUAGES → language_code
- MKTG.ASSET TYPE → asset_type
- master_content_info → width_px, height_px

Campaign-level fields (CAMPAIGN_BRAND, CAMPAIGN_MARKET) don't exist
on individual assets - they're on the campaign folder.

Using asset-level fields that are actually present.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 14:34:29 -04:00
DJP
27b5121512 Add detailed field logging to debug metadata extraction
Log every metadata field encountered to see:
- Which fields are in the metadata
- Why CAMPAIGN_BRAND and CAMPAIGN_MARKET aren't being found
- Structure of metadata categories

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 14:28:22 -04:00
DJP
b2d10b03aa Add more debug logging to trace upload folder and metadata structure 2025-10-29 14:26:57 -04:00
DJP
cccb4b20f2 Add comprehensive debug logging for metadata extraction
Debug what's being passed:
- AssetData keys available
- Upload folder ID value
- Master assets count and structure
- Asset metadata keys per file

This will show exactly what data is available for extraction.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 14:18:44 -04:00
DJP
105632316f Add debug logging for metadata extraction
Logging to troubleshoot why fields aren't being populated:
- Log extracted metadata values
- Log upload folder ID
- Log width/height extraction
- Check asset_content_info structure variations

Will show in logs what's being extracted vs what's NULL.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 14:14:18 -04:00
DJP
afe79bbbcc Extract and populate all database fields from DAM metadata
DatabaseClient now extracts and stores:
- brand_code, brand_name (from FERRERO.FIELD.CAMPAIGN_BRAND)
- country_code (from FERRERO.FIELD.CAMPAIGN_MARKET)
- language_code (to be extracted from filename or metadata)
- asset_type (from metadata fields)
- width_px, height_px (from asset_content_info.master_content)
- file_size_bytes (from asset data)
- mime_type (from asset data)
- upload_directory (Final Assets folder ID passed from workflow)

Handles both domain values and regular values.
Populates all available database columns from DAM metadata.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 14:06:14 -04:00
DJP
d54611abdb Remove Box metadata template attempts - use description field
Box metadata template consistently fails despite:
- Correct template key: ferrerodammetadata
- Correct field name: DAM-Metadata
- Valid JWT authentication
- Multiple test approaches

Template requires Box admin configuration to associate with app.

Using file description field instead (works reliably):
- DAM Asset ID
- Tracking ID reference
- Note about PostgreSQL storage

Full metadata JSON stored in PostgreSQL database description column (working).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 17:46:06 -04:00
DJP
bb93108420 Test Box metadata with plain Asset ID string (not JSON)
Testing if JSON encoding is the issue.
Sending just the plain Asset ID string to DAM-Metadata field.

Logs will show if plain string works vs JSON string.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 17:37:16 -04:00
DJP
b750a5d9c3 Skip Box metadata template for now - use description reliably
Box metadata template has configuration issues that need Box support:
- Template exists (ferrerodammetadata)
- Field exists (DAM-Metadata)
- But API rejects with 'invalid parameters' regardless of field name
- May need template to be associated with folder/app first

Using file description instead (works reliably):
- Stores DAM Asset ID
- Tracking ID reference
- Filename and MIME type

Full metadata JSON is stored in PostgreSQL database (confirmed working).

Can debug Box template configuration later with Box support.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 17:26:44 -04:00
DJP
d6356e984a Use exact Box metadata field name: DAM-Metadata
User confirmed template definition:
- Template Key: ferrerodammetadata
- Field Name: DAM-Metadata (with hyphen)

Using exact field name from template definition.
Should work now!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 17:21:03 -04:00
DJP
1aa4b49c61 Test more Box metadata field name variations
Trying variations based on common Box naming:
- dAMMetadata (mixed case)
- DAMMetadata (all caps start)
- dammetadata (all lowercase)
- DAM_Metadata (underscore)
- dam_metadata (lowercase underscore)
- metadata (simple)

Logs will show exactly which variation Box accepts.

User should check template definition for exact field key name.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 17:18:52 -04:00
DJP
2b42b4e42e Store DAM metadata JSON in database description field + test Box template
Database:
- Store full DAM metadata JSON in description field (5KB limit)
- Includes Box links and upload folder ID
- Full asset metadata preserved

Box Metadata Template Testing:
- Simplified to send just test string first
- Log endpoint and values being sent
- Try 5 field name variations
- Will identify correct field name from logs

Next test will show which field name works for Box template.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 17:09:58 -04:00
DJP
f002d734a7 Store upload folder ID in upload_directory column
Database now stores:
- upload_directory = Final Assets folder ID from DAM
- This is the target folder for A2→A3 uploads
- Can be retrieved later when uploading processed files back to DAM

Column exists in DB, now being populated correctly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 17:05:44 -04:00
DJP
c3d3878b3a Add Final Assets folder ID extraction and storage in database
Workflow Enhancement:
- When getting Master Assets, also find Final Assets folder
- Store Final Assets folder ID in session for later upload use
- Pass upload folder ID to database storage

Database Storage:
- Store upload folder ID in description field
- Ready for dedicated upload_directory column when added to DB schema

UI Update:
- Success message shows Final Assets folder was located

This ensures we have the upload target folder ID when needed for A2→A3 workflow.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 17:05:11 -04:00
DJP
3cdf33f929 Simplify Box metadata and try multiple field name variations
Box metadata changes:
- Simplified JSON (only essential fields, not full 2MB metadata)
- Try 4 field name variations: DAM-Metadata, DAMMetadata, damMetadata, dam_metadata
- Log which variation works
- Fallback to description if all fail

This will help identify the correct field name for the template.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 16:40:14 -04:00
DJP
576dd72081 Fix Box metadata template name and database columns
Box Metadata Template:
- Fixed template key: 'ferrerodammetadata' (lowercase)
- Fixed field name: 'DAM-Metadata' (exact from template)
- Should now properly attach metadata to Box files

Database Fix:
- Removed non-existent columns: box_file_id, box_url, dam_metadata_json
- Using only existing columns from master_assets table
- Storing Box URL in description field as workaround
- Will add proper columns in DB migration later

Next: Box metadata template should work, DB inserts should succeed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 16:23:38 -04:00
DJP
d8e542a569 Add PostgreSQL database integration and Box metadata template
Database Integration:
- IDGenerator now connects to PostgreSQL (localhost:5433)
- Generates tracking IDs with uniqueness check against master_assets table
- Fallback to random if database unavailable
- Direct PDO connection to ferrero_tracking database

DatabaseClient:
- Stores master assets in PostgreSQL
- Records: tracking_id, opentext_id, Box links, full metadata JSON
- Updates on conflict (upsert pattern)
- Stores box_file_id and box_url for reference

Box Metadata Enhancement:
- Uses Box metadata template API (enterprise/ferreroDAMMetadata)
- Stores full DAM metadata JSON in 'Ferrero-DAM-Metadata' field
- Fallback to file description if template not configured
- Handles template conflicts (updates existing)

Box Upload Results Now Show:
- Unique tracking ID (from database)
- Box file links (clickable)
- Database storage status
- ID source (database_direct, random, etc.)

Complete workflow: DAM → Download → Generate ID → Upload to Box → Store in DB

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 15:39:32 -04:00
DJP
0c799aebe7 Implement proper Box JWT authentication with RSA signing
Box now uses JWT (JSON Web Token) authentication:
- Signs JWT with RSA private key from config
- Uses RS256 algorithm
- Enterprise-level access
- No expiring developer tokens

JWT Flow:
1. Create JWT header with publicKeyID
2. Create claims with enterprise ID
3. Sign with encrypted private key + passphrase
4. Exchange JWT for access token
5. Token auto-refreshes as needed

Config loaded from: Box-config.json (complete JWT config)

This is the proper production-ready authentication method.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 14:07:52 -04:00
DJP
fd3676e8d0 Use Box developer token for now - OAuth app not configured
Box OAuth requires app to be configured for client credentials grant.
Using developer token as fallback (valid 60 minutes).

User needs to:
1. Go to https://app.box.com/developers/console
2. Generate new Developer Token
3. Update token in code (expires hourly)

OR configure Box app for OAuth 2.0 client credentials grant.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 13:51:57 -04:00
DJP
eb16f2f6e9 Remove enterprise subject from Box OAuth - not authorized
Box app is not configured for enterprise client credentials.
Using standard client_credentials grant without subject type.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 13:51:27 -04:00
DJP
c4ccbae765 Fix Box to use OAuth 2.0 client credentials instead of developer token
Box now uses proper OAuth flow:
- Client ID: l2atwxxq4xna7phcjr2uifm4mbah69qp
- Client Secret: 6XcuCQ6akpk9daE0UHaGSv3mSxWaER4l
- Enterprise ID: 43984435
- Grant type: client_credentials with box_subject_type

Loads credentials from Box-config.json for security.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 13:30:28 -04:00
DJP
52e7c4afc9 Add detailed Box API error logging 2025-10-28 13:27:26 -04:00
DJP
36d45ae188 Add Box.com integration for asset storage with unique ID tracking
New Box Integration Features:
- BoxClient class for Box API operations
- IDGenerator for 6-character unique IDs (A-Z a-z 0-9)
- Auto-create campaign folders in Box
- Rename files with unique ID suffix (filename_ID.ext)
- Upload metadata JSON to Box custom fields
- Track Box file IDs and URLs

Download Workflow Enhancement:
- New button: '📥📦 Download & Upload to Box'
- Downloads from DAM → Uploads to Box
- Each file gets unique 6-char ID
- Creates campaign folder: {campaign_id}_{campaign_name}
- Results show: original → renamed filename with ID

Box Configuration:
- Developer Token: e7Q1kS6rOM1tH2ezzCg4KgRfcyNW2JHI
- Root Folder: 348304357505
- OAuth creds in Box-config.json

ID Generation:
- Phase 1: Random 6-char (current)
- Phase 2: PostgreSQL DB via REST API (ready to integrate)

Metadata Storage:
- Stored in Box file description (custom metadata field later)
- Full DAM metadata JSON preserved
- Includes: asset_id, campaign info, all metadata fields

Ready for testing! Download workflow now stores assets in Box with tracking.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 13:11:49 -04:00
DJP
45b4067150 Create simplified uploader matching exact standalone script
AssetUploaderSimple - exact copy of standalone logic:
- Only 5 metadata fields (not 17)
- Same field order and structure
- Same cURL options
- Produces ~1200 byte payload like standalone

Test upload now uses AssetUploaderSimple for exact match.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 09:12:51 -04:00
DJP
276101a7db Add code version marker to verify cache 2025-10-28 09:05:20 -04:00
DJP
50c30139bf Add detailed logging to debug web vs CLI difference 2025-10-28 09:04:53 -04:00