diff --git a/csharp/coupon/GenerateCouponCodesBatch.cs b/csharp/coupon/GenerateCouponCodesBatch.cs new file mode 100644 index 0000000..909dbca --- /dev/null +++ b/csharp/coupon/GenerateCouponCodesBatch.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using com.ultracart.admin.v2.Api; +using com.ultracart.admin.v2.Model; + +namespace SdkSample.coupon +{ + /// + /// Batch Coupon Code Generator + /// Generates large batches of coupon codes by calling the API repeatedly + /// + /// Usage: bin/Debug/SdkSample.exe GenerateCouponCodesBatch [total_codes] [output_file] + /// Example: bin/Debug/SdkSample.exe GenerateCouponCodesBatch 10OFF 10000 codes.csv + /// + public class GenerateCouponCodesBatch + { + private const int BATCH_SIZE = 1000; + + public static void Execute(string[] args) + { + // Parse command line arguments + if (args.Length < 1) + { + Console.Error.WriteLine("Error: Merchant code is required"); + Console.Error.WriteLine("Usage: bin/Debug/SdkSample.exe GenerateCouponCodesBatch [total_codes] [output_file]"); + Console.Error.WriteLine("Example: bin/Debug/SdkSample.exe GenerateCouponCodesBatch 10OFF 10000 codes.csv"); + Environment.Exit(1); + } + + string merchantCode = args[0]; + int totalCodes = args.Length > 1 ? int.Parse(args[1]) : 10000; + string outputFile = args.Length > 2 ? args[2] : + $"coupon_codes_{merchantCode}_{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}.csv"; + + // Validate inputs + if (totalCodes < 1 || totalCodes > 100000) + { + Console.Error.WriteLine($"Error: total_codes must be between 1 and 100,000 (got {totalCodes})"); + Environment.Exit(1); + } + + // Display header + Console.WriteLine("Batch Coupon Code Generator"); + Console.WriteLine("============================="); + Console.WriteLine($"Merchant Code: {merchantCode}"); + Console.WriteLine($"Total Codes: {totalCodes}"); + Console.WriteLine($"Output File: {outputFile}"); + Console.WriteLine(); + + try + { + // Initialize API client + CouponApi couponApi = new CouponApi(Constants.ApiKey); + + // Look up coupon by merchant code + Console.WriteLine("Looking up coupon by merchant code..."); + CouponResponse couponResponse = couponApi.GetCouponByMerchantCode(merchantCode); + int? couponOid = couponResponse.Coupon.CouponOid; + Console.WriteLine($"Found coupon OID: {couponOid}"); + Console.WriteLine(); + + // Calculate batching + int numBatches = (int)Math.Ceiling((double)totalCodes / BATCH_SIZE); + List allCodes = new List(); + + Console.WriteLine($"Generating codes in batches of {BATCH_SIZE}..."); + DateTime startTime = DateTime.Now; + + // Generate codes in batches + for (int batchNum = 1; batchNum <= numBatches; batchNum++) + { + // Calculate batch size + int batchSize = (batchNum == numBatches) + ? totalCodes - (batchNum - 1) * BATCH_SIZE + : BATCH_SIZE; + + // Create request + CouponCodesRequest codesRequest = new CouponCodesRequest(); + codesRequest.Quantity = batchSize; + codesRequest.ExpirationDts = DateTime.UtcNow.AddDays(365).ToString("yyyy-MM-ddTHH:mm:ssK"); + + // Call API with retry logic + int retryCount = 0; + bool success = false; + + while (retryCount < 2 && !success) + { + try + { + CouponCodesResponse batchResponse = couponApi.GenerateCouponCodes(couponOid, codesRequest); + List batchCodes = batchResponse.CouponCodes; + allCodes.AddRange(batchCodes); + success = true; + + // Display progress + double elapsed = (DateTime.Now - startTime).TotalSeconds; + Console.WriteLine($"[{batchNum}/{numBatches}] Generated {batchSize} codes (Total: {allCodes.Count}) - Elapsed: {elapsed:F1}s"); + } + catch (Exception e) + { + retryCount++; + if (retryCount < 2) + { + Console.WriteLine($"Warning: Batch {batchNum} failed, retrying..."); + Thread.Sleep(2000); // exponential backoff + } + else + { + Console.Error.WriteLine($"Error: Failed to generate batch {batchNum} after retries"); + Console.Error.WriteLine($"Details: {e.Message}"); + Environment.Exit(1); + } + } + } + + // Rate limiting - sleep between batches (except last) + if (batchNum < numBatches) + { + Thread.Sleep(750); + } + } + + Console.WriteLine(); + Console.WriteLine("Writing codes to CSV file..."); + + // Write to CSV + try + { + using (StreamWriter writer = new StreamWriter(outputFile)) + { + // Write header + writer.WriteLine("code,generated_at,batch_number"); + + // Write codes + int batchNum = 1; + int codesInBatch = 0; + string timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssK"); + + foreach (string code in allCodes) + { + writer.WriteLine($"{code},{timestamp},{batchNum}"); + + codesInBatch++; + if (codesInBatch >= BATCH_SIZE) + { + batchNum++; + codesInBatch = 0; + } + } + } + + Console.WriteLine($"Successfully saved {allCodes.Count} codes to: {outputFile}"); + } + catch (Exception e) + { + Console.Error.WriteLine($"Error writing to file: {e.Message}"); + Environment.Exit(1); + } + + // Display summary + double totalTime = (DateTime.Now - startTime).TotalSeconds; + double avgTime = totalTime / numBatches; + + Console.WriteLine(); + Console.WriteLine("Summary:"); + Console.WriteLine($"- Merchant Code: {merchantCode}"); + Console.WriteLine($"- Coupon OID: {couponOid}"); + Console.WriteLine($"- Total Codes Generated: {allCodes.Count}"); + Console.WriteLine($"- Batches Processed: {numBatches}"); + Console.WriteLine($"- Total Time: {totalTime:F1}s"); + Console.WriteLine($"- Average per batch: {avgTime:F1}s"); + Console.WriteLine($"- Output File: {Path.GetFullPath(outputFile)}"); + } + catch (Exception ex) + { + Console.Error.WriteLine($"Error: Coupon with merchant code '{merchantCode}' not found"); + Console.Error.WriteLine($"Details: {ex.Message}"); + Environment.Exit(1); + } + } + } +} diff --git a/java/src/coupon/GenerateCouponCodesBatch.java b/java/src/coupon/GenerateCouponCodesBatch.java new file mode 100644 index 0000000..55fc575 --- /dev/null +++ b/java/src/coupon/GenerateCouponCodesBatch.java @@ -0,0 +1,176 @@ +package coupon; + +import com.ultracart.admin.v2.CouponApi; +import com.ultracart.admin.v2.models.*; +import com.ultracart.admin.v2.util.ApiException; +import common.Constants; + +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Paths; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; + +/** + * Batch Coupon Code Generator + * Generates large batches of coupon codes by calling the API repeatedly + * + * Usage: java -cp target/sdk_samples-1.0-SNAPSHOT.jar coupon.GenerateCouponCodesBatch [total_codes] [output_file] + * Example: java -cp target/sdk_samples-1.0-SNAPSHOT.jar coupon.GenerateCouponCodesBatch 10OFF 10000 codes.csv + */ +public class GenerateCouponCodesBatch { + private static final int BATCH_SIZE = 1000; + + public static void execute(String[] args) { + // Parse command line arguments + if (args.length < 1) { + System.err.println("Error: Merchant code is required"); + System.err.println("Usage: java -cp target/sdk_samples-1.0-SNAPSHOT.jar coupon.GenerateCouponCodesBatch [total_codes] [output_file]"); + System.err.println("Example: java -cp target/sdk_samples-1.0-SNAPSHOT.jar coupon.GenerateCouponCodesBatch 10OFF 10000 codes.csv"); + System.exit(1); + } + + String merchantCode = args[0]; + int totalCodes = args.length > 1 ? Integer.parseInt(args[1]) : 10000; + String outputFile = args.length > 2 ? args[2] : + "coupon_codes_" + merchantCode + "_" + System.currentTimeMillis() + ".csv"; + + // Validate inputs + if (totalCodes < 1 || totalCodes > 100000) { + System.err.println("Error: total_codes must be between 1 and 100,000 (got " + totalCodes + ")"); + System.exit(1); + } + + // Display header + System.out.println("Batch Coupon Code Generator"); + System.out.println("============================="); + System.out.println("Merchant Code: " + merchantCode); + System.out.println("Total Codes: " + totalCodes); + System.out.println("Output File: " + outputFile); + System.out.println(); + + try { + // Initialize API client + CouponApi couponApi = new CouponApi(Constants.API_KEY); + + // Look up coupon by merchant code + System.out.println("Looking up coupon by merchant code..."); + CouponResponse couponResponse = couponApi.getCouponByMerchantCode(merchantCode, null); + Integer couponOid = couponResponse.getCoupon().getCouponOid(); + System.out.println("Found coupon OID: " + couponOid); + System.out.println(); + + // Calculate batching + int numBatches = (int) Math.ceil((double) totalCodes / BATCH_SIZE); + List allCodes = new ArrayList<>(); + + System.out.println("Generating codes in batches of " + BATCH_SIZE + "..."); + long startTime = System.currentTimeMillis(); + + // Generate codes in batches + for (int batchNum = 1; batchNum <= numBatches; batchNum++) { + // Calculate batch size + int batchSize = (batchNum == numBatches) + ? totalCodes - (batchNum - 1) * BATCH_SIZE + : BATCH_SIZE; + + // Create request + CouponCodesRequest codesRequest = new CouponCodesRequest(); + codesRequest.setQuantity(batchSize); + codesRequest.setExpirationDts(Instant.now().plus(365, ChronoUnit.DAYS).toString()); + + // Call API with retry logic + int retryCount = 0; + boolean success = false; + + while (retryCount < 2 && !success) { + try { + CouponCodesResponse batchResponse = couponApi.generateCouponCodes(couponOid, codesRequest); + List batchCodes = batchResponse.getCouponCodes(); + allCodes.addAll(batchCodes); + success = true; + + // Display progress + double elapsed = (System.currentTimeMillis() - startTime) / 1000.0; + System.out.printf("[%d/%d] Generated %d codes (Total: %d) - Elapsed: %.1fs%n", + batchNum, numBatches, batchSize, allCodes.size(), elapsed); + } catch (ApiException e) { + retryCount++; + if (retryCount < 2) { + System.out.println("Warning: Batch " + batchNum + " failed, retrying..."); + Thread.sleep(2000); // exponential backoff + } else { + System.err.println("Error: Failed to generate batch " + batchNum + " after retries"); + System.err.println("Details: " + e.getMessage()); + System.exit(1); + } + } + } + + // Rate limiting - sleep between batches (except last) + if (batchNum < numBatches) { + Thread.sleep(750); + } + } + + System.out.println(); + System.out.println("Writing codes to CSV file..."); + + // Write to CSV + try (FileWriter writer = new FileWriter(outputFile)) { + // Write header + writer.write("code,generated_at,batch_number\n"); + + // Write codes + int batchNum = 1; + int codesInBatch = 0; + String timestamp = Instant.now().toString(); + + for (String code : allCodes) { + writer.write(code + "," + timestamp + "," + batchNum + "\n"); + + codesInBatch++; + if (codesInBatch >= BATCH_SIZE) { + batchNum++; + codesInBatch = 0; + } + } + + System.out.println("Successfully saved " + allCodes.size() + " codes to: " + outputFile); + } catch (IOException e) { + System.err.println("Error writing to file: " + e.getMessage()); + System.exit(1); + } + + // Display summary + double totalTime = (System.currentTimeMillis() - startTime) / 1000.0; + double avgTime = totalTime / numBatches; + + System.out.println(); + System.out.println("Summary:"); + System.out.println("- Merchant Code: " + merchantCode); + System.out.println("- Coupon OID: " + couponOid); + System.out.println("- Total Codes Generated: " + allCodes.size()); + System.out.println("- Batches Processed: " + numBatches); + System.out.printf("- Total Time: %.1fs%n", totalTime); + System.out.printf("- Average per batch: %.1fs%n", avgTime); + System.out.println("- Output File: " + Paths.get(outputFile).toAbsolutePath()); + + } catch (ApiException e) { + System.err.println("Error: Coupon with merchant code '" + merchantCode + "' not found"); + System.err.println("Details: " + e.getMessage()); + System.err.println("Status code: " + e.getCode()); + System.exit(1); + } catch (InterruptedException e) { + System.err.println("Error: Thread interrupted"); + System.err.println("Details: " + e.getMessage()); + System.exit(1); + } + } + + public static void main(String[] args) { + execute(args); + } +} diff --git a/javascript/coupon/generateCouponCodesBatch.js b/javascript/coupon/generateCouponCodesBatch.js new file mode 100644 index 0000000..667cb5b --- /dev/null +++ b/javascript/coupon/generateCouponCodesBatch.js @@ -0,0 +1,175 @@ +import { couponApi } from '../api.js'; +import { DateTime } from 'luxon'; +import * as fs from 'fs'; + +/** + * Batch Coupon Code Generator + * Generates large batches of coupon codes by calling the API repeatedly + * + * Usage: NODE_TLS_REJECT_UNAUTHORIZED=0 node coupon/generateCouponCodesBatch.js [total_codes] [output_file] + * Example: NODE_TLS_REJECT_UNAUTHORIZED=0 node coupon/generateCouponCodesBatch.js 10OFF 10000 codes.csv + */ + +export class GenerateCouponCodesBatch { + static async execute() { + // Parse command line arguments + const merchantCode = process.argv[2]; + const totalCodes = parseInt(process.argv[3] || '10000'); + const outputFile = process.argv[4] || `coupon_codes_${merchantCode}_${Date.now()}.csv`; + + // Validate inputs + if (!merchantCode) { + console.error('Error: Merchant code is required'); + console.error('Usage: node coupon/generateCouponCodesBatch.js [total_codes] [output_file]'); + console.error('Example: node coupon/generateCouponCodesBatch.js 10OFF 10000 codes.csv'); + process.exit(1); + } + + if (totalCodes < 1 || totalCodes > 100000) { + console.error(`Error: total_codes must be between 1 and 100,000 (got ${totalCodes})`); + process.exit(1); + } + + // Display header + console.log('Batch Coupon Code Generator'); + console.log('============================='); + console.log(`Merchant Code: ${merchantCode}`); + console.log(`Total Codes: ${totalCodes}`); + console.log(`Output File: ${outputFile}`); + console.log(''); + + try { + // Look up coupon by merchant code + console.log('Looking up coupon by merchant code...'); + const couponResponse = await new Promise((resolve, reject) => { + couponApi.getCouponByMerchantCode(merchantCode, function (error, data, response) { + if (error) { + reject(error); + } else { + resolve(data); + } + }); + }); + + const couponOid = couponResponse.coupon.coupon_oid; + console.log(`Found coupon OID: ${couponOid}`); + console.log(''); + + // Calculate batching + const BATCH_SIZE = 1000; + const numBatches = Math.ceil(totalCodes / BATCH_SIZE); + const allCodes = []; + + console.log(`Generating codes in batches of ${BATCH_SIZE}...`); + const startTime = Date.now(); + + // Generate codes in batches + for (let batchNum = 1; batchNum <= numBatches; batchNum++) { + // Calculate batch size + const batchSize = (batchNum === numBatches) + ? totalCodes - (batchNum - 1) * BATCH_SIZE + : BATCH_SIZE; + + // Create request + const codesRequest = { + quantity: batchSize, + expiration_dts: DateTime.utc().plus({ days: 365 }).toISO() + }; + + // Call API with retry logic + let retryCount = 0; + let success = false; + + while (retryCount < 2 && !success) { + try { + const batchResponse = await new Promise((resolve, reject) => { + couponApi.generateCouponCodes(couponOid, codesRequest, function (error, data, response) { + if (error) { + reject(error); + } else { + resolve(data); + } + }); + }); + + const batchCodes = batchResponse.coupon_codes; + allCodes.push(...batchCodes); + success = true; + + // Display progress + const elapsed = (Date.now() - startTime) / 1000; + console.log(`[${batchNum}/${numBatches}] Generated ${batchSize} codes (Total: ${allCodes.length}) - Elapsed: ${elapsed.toFixed(1)}s`); + } catch (error) { + retryCount++; + if (retryCount < 2) { + console.log(`Warning: Batch ${batchNum} failed, retrying...`); + await new Promise(resolve => setTimeout(resolve, 2000)); // exponential backoff + } else { + console.error(`Error: Failed to generate batch ${batchNum} after retries`); + console.error(`Details: ${error.message}`); + process.exit(1); + } + } + } + + // Rate limiting - sleep between batches (except last) + if (batchNum < numBatches) { + await new Promise(resolve => setTimeout(resolve, 750)); + } + } + + console.log(''); + console.log('Writing codes to CSV file...'); + + // Write to CSV + try { + let csvContent = 'code,generated_at,batch_number\n'; + + let batchNum = 1; + let codesInBatch = 0; + const timestamp = new Date().toISOString(); + + for (const code of allCodes) { + csvContent += `${code},${timestamp},${batchNum}\n`; + + codesInBatch++; + if (codesInBatch >= BATCH_SIZE) { + batchNum++; + codesInBatch = 0; + } + } + + fs.writeFileSync(outputFile, csvContent, 'utf8'); + console.log(`Successfully saved ${allCodes.length} codes to: ${outputFile}`); + } catch (error) { + console.error(`Error writing to file: ${error.message}`); + process.exit(1); + } + + // Display summary + const totalTime = (Date.now() - startTime) / 1000; + const avgTime = totalTime / numBatches; + + console.log(''); + console.log('Summary:'); + console.log(`- Merchant Code: ${merchantCode}`); + console.log(`- Coupon OID: ${couponOid}`); + console.log(`- Total Codes Generated: ${allCodes.length}`); + console.log(`- Batches Processed: ${numBatches}`); + console.log(`- Total Time: ${totalTime.toFixed(1)}s`); + console.log(`- Average per batch: ${avgTime.toFixed(1)}s`); + console.log(`- Output File: ${fs.realpathSync(outputFile)}`); + + } catch (error) { + if (error.message && error.message.includes('not found')) { + console.error(`Error: Coupon with merchant code '${merchantCode}' not found`); + } else { + console.error(`Error: ${error.message}`); + } + if (error.status) { + console.error(`Status: ${error.status}`); + } + process.exit(1); + } + } +} diff --git a/php/coupon/generateCouponCodesBatch.php b/php/coupon/generateCouponCodesBatch.php new file mode 100644 index 0000000..19f3c32 --- /dev/null +++ b/php/coupon/generateCouponCodesBatch.php @@ -0,0 +1,161 @@ + [total_codes] [output_file] + * Example: php coupon/generateCouponCodesBatch.php 10OFF 10000 codes.csv + */ + +use ultracart\v2\api\CouponApi; +use ultracart\v2\models\CouponCodesRequest; + +require_once '../vendor/autoload.php'; +require_once '../constants.php'; + +// Parse command line arguments +$merchant_code = $argv[1] ?? null; +$total_codes = isset($argv[2]) ? intval($argv[2]) : 10000; +$output_file = $argv[3] ?? "coupon_codes_{$merchant_code}_" . time() . ".csv"; + +// Validate inputs +if (!$merchant_code) { + fwrite(STDERR, "Error: Merchant code is required\n"); + fwrite(STDERR, "Usage: php coupon/generateCouponCodesBatch.php [total_codes] [output_file]\n"); + fwrite(STDERR, "Example: php coupon/generateCouponCodesBatch.php 10OFF 10000 codes.csv\n"); + exit(1); +} + +if ($total_codes < 1 || $total_codes > 100000) { + fwrite(STDERR, "Error: total_codes must be between 1 and 100,000 (got {$total_codes})\n"); + exit(1); +} + +// Display header +echo "Batch Coupon Code Generator\n"; +echo "=============================\n"; +echo "Merchant Code: {$merchant_code}\n"; +echo "Total Codes: {$total_codes}\n"; +echo "Output File: {$output_file}\n"; +echo "\n"; + +// Initialize API client +$coupon_api = CouponApi::usingApiKey(Constants::API_KEY); + +// Look up coupon by merchant code +echo "Looking up coupon by merchant code...\n"; +try { + $coupon_response = $coupon_api->getCouponByMerchantCode($merchant_code); + $coupon_oid = $coupon_response->getCoupon()->getCouponOid(); + echo "Found coupon OID: {$coupon_oid}\n"; + echo "\n"; +} catch (Exception $e) { + fwrite(STDERR, "Error: Coupon with merchant code '{$merchant_code}' not found\n"); + fwrite(STDERR, "Details: " . $e->getMessage() . "\n"); + exit(1); +} + +// Calculate batching +const BATCH_SIZE = 1000; +$num_batches = intval(ceil($total_codes / BATCH_SIZE)); +$all_codes = []; + +echo "Generating codes in batches of " . BATCH_SIZE . "...\n"; +$start_time = microtime(true); + +// Generate codes in batches +for ($batch_num = 1; $batch_num <= $num_batches; $batch_num++) { + // Calculate batch size + if ($batch_num == $num_batches) { + $batch_size = $total_codes - ($batch_num - 1) * BATCH_SIZE; + } else { + $batch_size = BATCH_SIZE; + } + + // Create request + $codes_request = new CouponCodesRequest(); + $codes_request->setQuantity($batch_size); + $codes_request->setExpirationDts(date('Y-m-d', strtotime('+365 days')) . "T00:00:00+00:00"); + + // Call API with retry logic + $retry_count = 0; + $success = false; + + while ($retry_count < 2 && !$success) { + try { + $batch_response = $coupon_api->generateCouponCodes($coupon_oid, $codes_request); + $batch_codes = $batch_response->getCouponCodes(); + $all_codes = array_merge($all_codes, $batch_codes); + $success = true; + + // Display progress + $elapsed = microtime(true) - $start_time; + echo "[{$batch_num}/{$num_batches}] Generated {$batch_size} codes (Total: " . count($all_codes) . ") - Elapsed: " . number_format($elapsed, 1) . "s\n"; + } catch (Exception $e) { + $retry_count++; + if ($retry_count < 2) { + echo "Warning: Batch {$batch_num} failed, retrying...\n"; + sleep(2); // exponential backoff + } else { + fwrite(STDERR, "Error: Failed to generate batch {$batch_num} after retries\n"); + fwrite(STDERR, "Details: " . $e->getMessage() . "\n"); + exit(1); + } + } + } + + // Rate limiting - sleep between batches (except last) + if ($batch_num < $num_batches) { + usleep(750000); // 750ms in microseconds + } +} + +echo "\n"; +echo "Writing codes to CSV file...\n"; + +// Write to CSV +try { + $file_handle = fopen($output_file, 'w'); + if ($file_handle === false) { + throw new Exception("Could not open file for writing"); + } + + // Write header + fputcsv($file_handle, ['code', 'generated_at', 'batch_number']); + + // Write codes + $batch_num = 1; + $codes_in_batch = 0; + $timestamp = gmdate('Y-m-d\TH:i:s\Z'); + + foreach ($all_codes as $code) { + fputcsv($file_handle, [$code, $timestamp, $batch_num]); + + $codes_in_batch++; + if ($codes_in_batch >= BATCH_SIZE) { + $batch_num++; + $codes_in_batch = 0; + } + } + + fclose($file_handle); + echo "Successfully saved " . count($all_codes) . " codes to: {$output_file}\n"; +} catch (Exception $e) { + fwrite(STDERR, "Error writing to file: " . $e->getMessage() . "\n"); + exit(1); +} + +// Display summary +$total_time = microtime(true) - $start_time; +$avg_time = $total_time / $num_batches; + +echo "\n"; +echo "Summary:\n"; +echo "- Merchant Code: {$merchant_code}\n"; +echo "- Coupon OID: {$coupon_oid}\n"; +echo "- Total Codes Generated: " . count($all_codes) . "\n"; +echo "- Batches Processed: {$num_batches}\n"; +echo "- Total Time: " . number_format($total_time, 1) . "s\n"; +echo "- Average per batch: " . number_format($avg_time, 1) . "s\n"; +echo "- Output File: " . realpath($output_file) . "\n"; diff --git a/python/coupon/generate_coupon_codes_batch.py b/python/coupon/generate_coupon_codes_batch.py new file mode 100644 index 0000000..209158d --- /dev/null +++ b/python/coupon/generate_coupon_codes_batch.py @@ -0,0 +1,149 @@ +from ultracart.apis import CouponApi +from ultracart.models import CouponCodesRequest +import ultracart.rest +from samples import api_client +from datetime import datetime, timedelta +import sys +import csv +import time +import os +import math + +# Batch Coupon Code Generator +# Generates large batches of coupon codes by calling the API repeatedly +# Usage: PYTHONPATH=. python coupon/generate_coupon_codes_batch.py [total_codes] [output_file] +# Example: PYTHONPATH=. python coupon/generate_coupon_codes_batch.py 10OFF 10000 codes.csv + +# Parse command line arguments +merchant_code = sys.argv[1] if len(sys.argv) > 1 else None +total_codes = int(sys.argv[2]) if len(sys.argv) > 2 else 10000 +output_file = sys.argv[3] if len(sys.argv) > 3 else f"coupon_codes_{merchant_code}_{int(time.time())}.csv" + +# Validate inputs +if not merchant_code: + print('Error: Merchant code is required') + print('Usage: PYTHONPATH=. python coupon/generate_coupon_codes_batch.py [total_codes] [output_file]') + print('Example: PYTHONPATH=. python coupon/generate_coupon_codes_batch.py 10OFF 10000 codes.csv') + sys.exit(1) + +if total_codes < 1 or total_codes > 100000: + print(f'Error: total_codes must be between 1 and 100,000 (got {total_codes})') + sys.exit(1) + +# Display header +print('Batch Coupon Code Generator') +print('=============================') +print(f'Merchant Code: {merchant_code}') +print(f'Total Codes: {total_codes}') +print(f'Output File: {output_file}') +print('') + +# Initialize API client +coupon_api = CouponApi(api_client()) + +# Look up coupon by merchant code +print('Looking up coupon by merchant code...') +try: + coupon_response = coupon_api.get_coupon_by_merchant_code(merchant_code) + coupon_oid = coupon_response.coupon.coupon_oid + print(f'Found coupon OID: {coupon_oid}') + print('') +except ultracart.rest.ApiException as e: + print(f"Error: Coupon with merchant code '{merchant_code}' not found") + print(f'Details: {e}') + if hasattr(e, 'status'): + print(f'Status: {e.status}') + sys.exit(1) + +# Calculate batching +BATCH_SIZE = 1000 +num_batches = math.ceil(total_codes / BATCH_SIZE) +all_codes = [] + +print(f'Generating codes in batches of {BATCH_SIZE}...') +start_time = time.time() + +# Generate codes in batches +for batch_num in range(1, num_batches + 1): + # Calculate batch size + if batch_num == num_batches: + batch_size = total_codes - (batch_num - 1) * BATCH_SIZE + else: + batch_size = BATCH_SIZE + + # Create request + codes_request = CouponCodesRequest() + codes_request.quantity = batch_size + expiration_date = (datetime.now() + timedelta(days=365)).strftime('%Y-%m-%dT%H:%M:%S+00:00') + codes_request.expiration_dts = expiration_date + + # Call API with retry logic + retry_count = 0 + success = False + + while retry_count < 2 and not success: + try: + batch_response = coupon_api.generate_coupon_codes(coupon_oid, codes_request) + batch_codes = batch_response.coupon_codes + all_codes.extend(batch_codes) + success = True + + # Display progress + elapsed = time.time() - start_time + print(f'[{batch_num}/{num_batches}] Generated {batch_size} codes (Total: {len(all_codes)}) - Elapsed: {elapsed:.1f}s') + except ultracart.rest.ApiException as e: + retry_count += 1 + if retry_count < 2: + print(f'Warning: Batch {batch_num} failed, retrying...') + time.sleep(2) # exponential backoff + else: + print(f'Error: Failed to generate batch {batch_num} after retries') + print(f'Details: {e}') + sys.exit(1) + + # Rate limiting - sleep between batches (except last) + if batch_num < num_batches: + time.sleep(0.75) + +print('') +print('Writing codes to CSV file...') + +# Write to CSV +try: + with open(output_file, 'w', newline='') as csvfile: + csv_writer = csv.writer(csvfile) + + # Write header + csv_writer.writerow(['code', 'generated_at', 'batch_number']) + + # Write codes + batch_num = 1 + codes_in_batch = 0 + timestamp = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') + + for code in all_codes: + csv_writer.writerow([code, timestamp, batch_num]) + + codes_in_batch += 1 + if codes_in_batch >= BATCH_SIZE: + batch_num += 1 + codes_in_batch = 0 + + print(f'Successfully saved {len(all_codes)} codes to: {output_file}') +except Exception as e: + print(f'Error writing to file: {e}') + sys.exit(1) + +# Display summary +total_time = time.time() - start_time +avg_time = total_time / num_batches + +print('') +print('Summary:') +print(f'- Merchant Code: {merchant_code}') +print(f'- Coupon OID: {coupon_oid}') +print(f'- Total Codes Generated: {len(all_codes)}') +print(f'- Batches Processed: {num_batches}') +print(f'- Total Time: {total_time:.1f}s') +print(f'- Average per batch: {avg_time:.1f}s') +print(f'- Output File: {os.path.abspath(output_file)}') diff --git a/ruby/coupon/generate_coupon_codes_batch.rb b/ruby/coupon/generate_coupon_codes_batch.rb new file mode 100644 index 0000000..5288d39 --- /dev/null +++ b/ruby/coupon/generate_coupon_codes_batch.rb @@ -0,0 +1,150 @@ +require_relative '../constants' +require 'ultracart_api' +require 'csv' +require 'date' + +# Batch Coupon Code Generator +# Generates large batches of coupon codes by calling the API repeatedly +# Usage: ruby generate_coupon_codes_batch.rb [total_codes] [output_file] +# Example: ruby generate_coupon_codes_batch.rb 10OFF 10000 codes.csv + +# Parse command line arguments +merchant_code = ARGV[0] +total_codes = (ARGV[1] || '10000').to_i +output_file = ARGV[2] || "coupon_codes_#{merchant_code}_#{Time.now.to_i}.csv" + +# Validate inputs +if merchant_code.nil? || merchant_code.empty? + puts 'Error: Merchant code is required' + puts 'Usage: ruby generate_coupon_codes_batch.rb [total_codes] [output_file]' + puts 'Example: ruby generate_coupon_codes_batch.rb 10OFF 10000 codes.csv' + exit 1 +end + +if total_codes < 1 || total_codes > 100000 + puts "Error: total_codes must be between 1 and 100,000 (got #{total_codes})" + exit 1 +end + +# Display header +puts 'Batch Coupon Code Generator' +puts '=============================' +puts "Merchant Code: #{merchant_code}" +puts "Total Codes: #{total_codes}" +puts "Output File: #{output_file}" +puts '' + +# Initialize API client +coupon_api = UltracartClient::CouponApi.new_using_api_key(Constants::API_KEY) + +# Look up coupon by merchant code +puts 'Looking up coupon by merchant code...' +begin + coupon_response = coupon_api.get_coupon_by_merchant_code(merchant_code) + coupon_oid = coupon_response.coupon.coupon_oid + puts "Found coupon OID: #{coupon_oid}" + puts '' +rescue UltracartClient::ApiError => e + puts "Error: Coupon with merchant code '#{merchant_code}' not found" + puts "Details: #{e.message}" + puts "Status code: #{e.code}" if e.respond_to?(:code) + exit 1 +end + +# Calculate batching +BATCH_SIZE = 1000 +num_batches = (total_codes.to_f / BATCH_SIZE).ceil +all_codes = [] + +puts "Generating codes in batches of #{BATCH_SIZE}..." +start_time = Time.now + +# Generate codes in batches +(1..num_batches).each do |batch_num| + # Calculate batch size + batch_size = if batch_num == num_batches + total_codes - (batch_num - 1) * BATCH_SIZE + else + BATCH_SIZE + end + + # Create request + codes_request = UltracartClient::CouponCodesRequest.new + codes_request.quantity = batch_size + codes_request.expiration_dts = (Date.today + 365).strftime('%Y-%m-%d') + 'T00:00:00+00:00' + + # Call API with retry logic + retry_count = 0 + success = false + + while retry_count < 2 && !success + begin + batch_response = coupon_api.generate_coupon_codes(coupon_oid, codes_request) + batch_codes = batch_response.coupon_codes + all_codes.concat(batch_codes) + success = true + + # Display progress + elapsed = Time.now - start_time + puts "[#{batch_num}/#{num_batches}] Generated #{batch_size} codes (Total: #{all_codes.length}) - Elapsed: #{elapsed.round(1)}s" + rescue UltracartClient::ApiError => e + retry_count += 1 + if retry_count < 2 + puts "Warning: Batch #{batch_num} failed, retrying..." + sleep(2) # exponential backoff + else + puts "Error: Failed to generate batch #{batch_num} after retries" + puts "Details: #{e.message}" + exit 1 + end + end + end + + # Rate limiting - sleep between batches (except last) + sleep(0.75) if batch_num < num_batches +end + +puts '' +puts 'Writing codes to CSV file...' + +# Write to CSV +begin + CSV.open(output_file, 'w') do |csv| + # Write header + csv << ['code', 'generated_at', 'batch_number'] + + # Write codes + batch_num = 1 + codes_in_batch = 0 + timestamp = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ') + + all_codes.each do |code| + csv << [code, timestamp, batch_num] + + codes_in_batch += 1 + if codes_in_batch >= BATCH_SIZE + batch_num += 1 + codes_in_batch = 0 + end + end + end + + puts "Successfully saved #{all_codes.length} codes to: #{output_file}" +rescue StandardError => e + puts "Error writing to file: #{e.message}" + exit 1 +end + +# Display summary +total_time = Time.now - start_time +avg_time = total_time / num_batches + +puts '' +puts 'Summary:' +puts "- Merchant Code: #{merchant_code}" +puts "- Coupon OID: #{coupon_oid}" +puts "- Total Codes Generated: #{all_codes.length}" +puts "- Batches Processed: #{num_batches}" +puts "- Total Time: #{total_time.round(1)}s" +puts "- Average per batch: #{avg_time.round(1)}s" +puts "- Output File: #{File.absolute_path(output_file)}" diff --git a/typescript/coupon/GenerateCouponCodesBatch.ts b/typescript/coupon/GenerateCouponCodesBatch.ts new file mode 100644 index 0000000..2059b9b --- /dev/null +++ b/typescript/coupon/GenerateCouponCodesBatch.ts @@ -0,0 +1,163 @@ +import { couponApi } from '../api'; +import { CouponResponse, CouponCodesRequest, CouponCodesResponse } from 'ultracart_rest_api_v2_typescript'; +import { DateTime } from 'luxon'; +import * as fs from 'fs'; + +/** + * Batch Coupon Code Generator + * Generates large batches of coupon codes by calling the API repeatedly + * + * Usage: NODE_TLS_REJECT_UNAUTHORIZED=0 ts-node coupon/GenerateCouponCodesBatch.ts [total_codes] [output_file] + * Example: NODE_TLS_REJECT_UNAUTHORIZED=0 ts-node coupon/GenerateCouponCodesBatch.ts 10OFF 10000 codes.csv + */ + +export class GenerateCouponCodesBatch { + public static async execute(): Promise { + // Parse command line arguments + const merchantCode: string = process.argv[2]; + const totalCodes: number = parseInt(process.argv[3] || '10000'); + const outputFile: string = process.argv[4] || `coupon_codes_${merchantCode}_${Date.now()}.csv`; + + // Validate inputs + if (!merchantCode) { + console.error('Error: Merchant code is required'); + console.error('Usage: ts-node coupon/GenerateCouponCodesBatch.ts [total_codes] [output_file]'); + console.error('Example: ts-node coupon/GenerateCouponCodesBatch.ts 10OFF 10000 codes.csv'); + process.exit(1); + } + + if (totalCodes < 1 || totalCodes > 100000) { + console.error(`Error: total_codes must be between 1 and 100,000 (got ${totalCodes})`); + process.exit(1); + } + + // Display header + console.log('Batch Coupon Code Generator'); + console.log('============================='); + console.log(`Merchant Code: ${merchantCode}`); + console.log(`Total Codes: ${totalCodes}`); + console.log(`Output File: ${outputFile}`); + console.log(''); + + try { + // Look up coupon by merchant code + console.log('Looking up coupon by merchant code...'); + const couponResponse: CouponResponse = await couponApi.getCouponByMerchantCode({ merchantCode: merchantCode }); + + const couponOid: number = couponResponse.coupon!.coupon_oid!; + console.log(`Found coupon OID: ${couponOid}`); + console.log(''); + + // Calculate batching + const BATCH_SIZE: number = 1000; + const numBatches: number = Math.ceil(totalCodes / BATCH_SIZE); + const allCodes: string[] = []; + + console.log(`Generating codes in batches of ${BATCH_SIZE}...`); + const startTime: number = Date.now(); + + // Generate codes in batches + for (let batchNum = 1; batchNum <= numBatches; batchNum++) { + // Calculate batch size + const batchSize: number = (batchNum === numBatches) + ? totalCodes - (batchNum - 1) * BATCH_SIZE + : BATCH_SIZE; + + // Create request + const codesRequest: CouponCodesRequest = { + quantity: batchSize, + expiration_dts: DateTime.utc().plus({ days: 365 }).toISO() + }; + + // Call API with retry logic + let retryCount: number = 0; + let success: boolean = false; + + while (retryCount < 2 && !success) { + try { + const batchResponse: CouponCodesResponse = await couponApi.generateCouponCodes({ + couponOid: couponOid, + couponCodesRequest: codesRequest + }); + + const batchCodes: string[] = batchResponse.coupon_codes!; + allCodes.push(...batchCodes); + success = true; + + // Display progress + const elapsed: number = (Date.now() - startTime) / 1000; + console.log(`[${batchNum}/${numBatches}] Generated ${batchSize} codes (Total: ${allCodes.length}) - Elapsed: ${elapsed.toFixed(1)}s`); + } catch (error: any) { + retryCount++; + if (retryCount < 2) { + console.log(`Warning: Batch ${batchNum} failed, retrying...`); + await new Promise(resolve => setTimeout(resolve, 2000)); // exponential backoff + } else { + console.error(`Error: Failed to generate batch ${batchNum} after retries`); + console.error(`Details: ${error.message || error}`); + process.exit(1); + } + } + } + + // Rate limiting - sleep between batches (except last) + if (batchNum < numBatches) { + await new Promise(resolve => setTimeout(resolve, 750)); + } + } + + console.log(''); + console.log('Writing codes to CSV file...'); + + // Write to CSV + try { + let csvContent: string = 'code,generated_at,batch_number\n'; + + let batchNum: number = 1; + let codesInBatch: number = 0; + const timestamp: string = new Date().toISOString(); + + for (const code of allCodes) { + csvContent += `${code},${timestamp},${batchNum}\n`; + + codesInBatch++; + if (codesInBatch >= BATCH_SIZE) { + batchNum++; + codesInBatch = 0; + } + } + + fs.writeFileSync(outputFile, csvContent, 'utf8'); + console.log(`Successfully saved ${allCodes.length} codes to: ${outputFile}`); + } catch (error: any) { + console.error(`Error writing to file: ${error.message || error}`); + process.exit(1); + } + + // Display summary + const totalTime: number = (Date.now() - startTime) / 1000; + const avgTime: number = totalTime / numBatches; + + console.log(''); + console.log('Summary:'); + console.log(`- Merchant Code: ${merchantCode}`); + console.log(`- Coupon OID: ${couponOid}`); + console.log(`- Total Codes Generated: ${allCodes.length}`); + console.log(`- Batches Processed: ${numBatches}`); + console.log(`- Total Time: ${totalTime.toFixed(1)}s`); + console.log(`- Average per batch: ${avgTime.toFixed(1)}s`); + console.log(`- Output File: ${fs.realpathSync(outputFile)}`); + + } catch (error: any) { + if (error.message && error.message.includes('not found')) { + console.error(`Error: Coupon with merchant code '${merchantCode}' not found`); + } else { + console.error(`Error: ${error.message || error}`); + } + if (error.status) { + console.error(`Status: ${error.status}`); + } + process.exit(1); + } + } +}