loreal-global-kickoff/process-csv.php
DJP 80b170a735 Add Global to Local CSV transformation backend
Services Created:
- OMGService.php: OMG API integration with detailed error handling
- CSVTransformer.php: CSV parsing and transformation logic
- EmailService.php: Mailgun email notifications
- process-csv.php: Multi-stage CSV processing with progress tracking
- upload-to-box.php: Box upload with approval workflow

Features:
- Comprehensive validation at each stage (upload, parse, campaign, API, transform)
- Detailed error reporting with actionable messages
- Warning system for non-critical issues
- Progress tracking through all stages
- Session-based CSV storage for preview before upload
- Date transformation (parse + add 1 month per blueprint)
- 16x market multiplication per ISO codes
- Business unit mapping per Make.com blueprint logic

Dependencies Added:
- league/csv for CSV parsing
- nesbot/carbon for date manipulation

Configuration:
- Added global_to_local settings (ISO codes, business unit map)
- Added omg_api settings (placeholder for API key)
- Added email settings (Mailgun placeholders)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 16:44:48 -05:00

243 lines
8.3 KiB
PHP

<?php
/**
* Process CSV Upload
* Handles CSV transformation with detailed progress tracking and error reporting
*/
error_reporting(E_ALL);
ini_set('display_errors', 1);
header('Content-Type: application/json');
try {
// Load dependencies
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/AuthMiddleware.php';
require_once __DIR__ . '/CSVTransformer.php';
require_once __DIR__ . '/OMGService.php';
require_once __DIR__ . '/EmailService.php';
// Authenticate
$auth = new AuthMiddleware();
$user = $auth->requireAuth();
// Check if file was uploaded
if (!isset($_FILES['csvFile'])) {
echo json_encode([
'success' => false,
'stage' => 'upload',
'error' => 'No file uploaded',
'details' => 'Please select a CSV file to upload'
]);
exit;
}
$file = $_FILES['csvFile'];
$transformer = new CSVTransformer();
$omgService = new OMGService();
$emailService = new EmailService();
$progress = [];
// STAGE 1: Validate Upload
$progress[] = ['stage' => 'upload', 'status' => 'processing', 'message' => 'Validating file upload...'];
$validation = $transformer->validateUpload($file);
if (!$validation['valid']) {
echo json_encode([
'success' => false,
'stage' => 'upload',
'error' => 'File validation failed',
'details' => implode('; ', $validation['errors']),
'action' => 'Please upload a valid CSV file (max 5MB)',
'progress' => $progress
]);
exit;
}
$filename = $validation['filename'];
$progress[] = ['stage' => 'upload', 'status' => 'success', 'message' => 'File uploaded: ' . $filename];
// STAGE 2: Parse CSV
$progress[] = ['stage' => 'parse', 'status' => 'processing', 'message' => 'Parsing CSV file...'];
$parseResult = $transformer->parseCSV($file['tmp_name']);
if (!$parseResult['success']) {
echo json_encode([
'success' => false,
'stage' => 'parse',
'error' => $parseResult['error'],
'details' => $parseResult['details'] ?? '',
'action' => $parseResult['action'] ?? 'Check CSV format',
'progress' => $progress
]);
exit;
}
$rowCount = $parseResult['rowCount'];
$progress[] = ['stage' => 'parse', 'status' => 'success', 'message' => "Parsed $rowCount data rows"];
// STAGE 3: Extract Campaign Number
$progress[] = ['stage' => 'campaign', 'status' => 'processing', 'message' => 'Extracting campaign number...'];
$campaignResult = $transformer->extractCampaignNumber($filename);
if (!$campaignResult['success']) {
echo json_encode([
'success' => false,
'stage' => 'campaign',
'error' => $campaignResult['error'],
'details' => $campaignResult['details'] ?? '',
'action' => $campaignResult['action'] ?? 'Fix filename format',
'progress' => $progress
]);
exit;
}
$campaignNumber = $campaignResult['campaignNumber'];
$message = 'Campaign number: ' . $campaignNumber;
if (isset($campaignResult['warning'])) {
$message .= ' (warning: ' . $campaignResult['warning'] . ')';
}
$progress[] = ['stage' => 'campaign', 'status' => 'success', 'message' => $message];
// STAGE 4: Call OMG API
$progress[] = ['stage' => 'omg_api', 'status' => 'processing', 'message' => 'Looking up campaign in OMG...'];
$projectResult = $omgService->getProject($campaignNumber);
if (!$projectResult['success']) {
echo json_encode([
'success' => false,
'stage' => 'omg_api',
'error' => $projectResult['error'],
'details' => $projectResult['details'] ?? '',
'action' => $projectResult['action'] ?? 'Check OMG API configuration',
'httpCode' => $projectResult['httpCode'] ?? 0,
'endpoint' => $omgService->config['base_url'] . '/getProject?project_number=' . $campaignNumber,
'progress' => $progress
]);
exit;
}
$projectData = $projectResult['data'];
$progress[] = ['stage' => 'omg_api', 'status' => 'success', 'message' => 'Retrieved campaign data from OMG'];
// STAGE 5: Map Business Unit
$progress[] = ['stage' => 'business_unit', 'status' => 'processing', 'message' => 'Mapping business unit...'];
$businessArea = $omgService->getBusinessArea($projectData);
if (empty($businessArea)) {
echo json_encode([
'success' => false,
'stage' => 'business_unit',
'error' => 'Business area not found in OMG response',
'details' => 'The OMG API response did not contain a business_area field',
'action' => 'Check if campaign data is complete in OMG system',
'omgResponse' => $projectData,
'progress' => $progress
]);
exit;
}
$mappingResult = $omgService->mapBusinessUnit($businessArea);
if (!$mappingResult['success']) {
// Non-fatal warning - continue with "ERROR" value
$progress[] = [
'stage' => 'business_unit',
'status' => 'warning',
'message' => 'Business unit "' . $businessArea . '" not recognized - using "ERROR"',
'warning' => $mappingResult['error']
];
$businessUnit = 'ERROR';
} else {
$businessUnit = $mappingResult['businessUnit'];
$progress[] = [
'stage' => 'business_unit',
'status' => 'success',
'message' => 'Business unit: ' . $businessUnit
];
}
// STAGE 6: Transform Data
$progress[] = ['stage' => 'transform', 'status' => 'processing', 'message' => 'Transforming data for 16 markets...'];
$transformResult = $transformer->transformData($parseResult['rows'], $campaignNumber, $businessUnit);
if (!$transformResult['success']) {
echo json_encode([
'success' => false,
'stage' => 'transform',
'error' => 'Data transformation failed',
'details' => count($transformResult['errors']) . ' error(s) found',
'errors' => $transformResult['errors'],
'warnings' => $transformResult['warnings'],
'action' => 'Review errors and fix CSV data',
'progress' => $progress
]);
exit;
}
$outputRowCount = $transformResult['outputRowCount'];
$progress[] = [
'stage' => 'transform',
'status' => 'success',
'message' => "Transformed {$rowCount} rows into {$outputRowCount} regional rows"
];
// Generate CSV output
use League\Csv\Writer;
$csv = Writer::createFromString();
$csv->insertOne(array_keys($transformResult['rows'][0])); // Headers
$csv->insertAll($transformResult['rows']); // Data
$csvContent = $csv->toString();
// Generate filename (use first ISO code for single file preview)
$outputFilename = $transformer->generateFilename($campaignNumber, $businessUnit, $transformer->config['iso_codes'][0]);
// Store in session for later upload
session_start();
$_SESSION['processed_csv'] = [
'content' => $csvContent,
'filename' => $outputFilename,
'campaignNumber' => $campaignNumber,
'businessUnit' => $businessUnit,
'rowCount' => $outputRowCount
];
// Return success with preview data
echo json_encode([
'success' => true,
'stage' => 'complete',
'data' => [
'inputRows' => $rowCount,
'outputRows' => $outputRowCount,
'campaignNumber' => $campaignNumber,
'businessUnit' => $businessUnit,
'filename' => $outputFilename,
'preview' => array_slice($transformResult['rows'], 0, 50), // First 50 rows for preview
'csvContent' => $csvContent,
'warnings' => $transformResult['warnings']
],
'progress' => $progress
]);
} catch (Exception $e) {
error_log('Process CSV exception: ' . $e->getMessage());
error_log('Stack trace: ' . $e->getTraceAsString());
echo json_encode([
'success' => false,
'stage' => 'exception',
'error' => 'Unexpected error occurred',
'details' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
]);
}