// COMPLETE FIXED GOOGLE APPS SCRIPT CODE FOR PDF GENERATION
// ================================================================
//
// **FIXED ISSUES:**
// 1. Now properly replaces {{variable}} instead of just variable
// 2. Personality trait scores now show just the percentage, not "{{Trait_score}}"
// 3. Includes complete doPost function for HTTP request handling
//
// INSTRUCTIONS:
// 1. Go to https://script.google.com
// 2. Open your existing "TalentG PDF Generator" project
// 3. Replace ALL code in Code.gs with this COMPLETE code
// 4. Click Save
// 5. TEST: Select testPDFGeneration function and click "Run" to test
// 6. Click Deploy > Manage deployments
// 7. Click the pencil icon to edit your existing deployment
// 8. Click Deploy to update
//
// This updated code will properly replace variables like:
// {{user_category}} -> "Postgraduate"
// {{strong_trait1_score}} -> "75"
// Configuration
const CONFIG = {
TEMPLATE_DOC_ID: '1zt9ZG-C5ScNNR8X-Ssnq-MFREtJUZyGAypAKBM2psAU',
MAX_CONCURRENT_REQUESTS: 5,
RATE_LIMIT_WINDOW: 60000, // 1 minute
MAX_REQUESTS_PER_WINDOW: 20
};
// Rate limiting storage
const rateLimitStore = PropertiesService.getScriptProperties();
/**
* Main doPost function - handles PDF generation requests
*/
function doPost(e) {
console.log('=== PDF Generation Request Started ===');
console.log('Request data:', JSON.stringify(e, null, 2));
try {
// Parse request data
const requestData = JSON.parse(e.postData.contents);
console.log('Parsed request data:', requestData);
// Check rate limiting
if (!checkRateLimit()) {
return createResponse(false, null, 'Rate limit exceeded. Please try again later.');
}
// Validate required fields
if (!requestData.assessmentId || !requestData.placeholders) {
return createResponse(false, null, 'Missing required fields: assessmentId and placeholders');
}
// Generate PDF
const result = generateStrengthFinderPDF(requestData);
if (result.success) {
console.log('=== PDF Generation Completed Successfully ===');
return createResponse(true, result.pdfUrl, null, result.fileName);
} else {
console.log('=== PDF Generation Failed ===');
return createResponse(false, null, result.error);
}
} catch (error) {
console.error('Error in doPost:', error);
return createResponse(false, null, `Server error: ${error.message}`);
}
}
function generateStrengthFinderPDF(requestData) {
// Validate input parameters
if (!requestData) {
throw new Error('❌ generateStrengthFinderPDF called without requestData. Use testPDFGeneration() for testing instead.');
}
if (!requestData.assessmentId) {
throw new Error('assessmentId is required in requestData');
}
if (!requestData.placeholders) {
throw new Error('placeholders object is required in requestData');
}
console.log('Starting PDF generation for assessment:', requestData.assessmentId);
try {
// Extract user details for filename
const userName = requestData.userName || requestData.placeholders['user_name'] || 'Student';
const sanitizedName = userName.replace(/[^a-zA-Z0-9\s]/g, '').replace(/\s+/g, '_');
// Format timestamp for filename (DD-MM-YYYY_HH-MM format)
const date = new Date();
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0');
const year = date.getFullYear();
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
// Create readable filename
const fileName = `Strength_Finder_Report_${sanitizedName}_${day}-${month}-${year}_${hours}-${minutes}.pdf`;
console.log('Generated filename:', fileName);
// Copy template document
console.log('Copying template document...');
const templateFile = DriveApp.getFileById(CONFIG.TEMPLATE_DOC_ID);
const copiedFile = templateFile.makeCopy(fileName);
const docId = copiedFile.getId();
console.log('Template copied, new doc ID:', docId);
// Open the copied document
const doc = DocumentApp.openById(docId);
const body = doc.getBody();
// Replace placeholders - CRITICAL FIX: Add curly braces around placeholder keys
console.log('Replacing placeholders...');
console.log('Placeholders to replace:', Object.keys(requestData.placeholders));
for (const [placeholder, value] of Object.entries(requestData.placeholders)) {
// FIX: Add curly braces around the placeholder key to match template format
const templatePlaceholder = `{{${placeholder}}}`;
console.log(`Replacing ${templatePlaceholder} with: "${value}"`);
body.replaceText(templatePlaceholder, value || '');
}
// Save the document
doc.saveAndClose();
console.log('Document saved and closed');
// Export as PDF and set proper permissions
console.log('Exporting as PDF...');
const pdfBlob = DriveApp.getFileById(docId).getBlob().setName(fileName);
// Create a new PDF file in a shared folder instead of using download URL
console.log('Creating PDF file in shared folder...');
// Use a shared folder for PDFs (you might want to create a specific folder for this)
// For now, we'll create it in the root and set permissions
const pdfFile = DriveApp.createFile(pdfBlob);
const pdfFileId = pdfFile.getId();
// Set file to be viewable by anyone with the link
pdfFile.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
// Get a more reliable download URL
const pdfUrl = `https://drive.google.com/uc?export=download&id=${pdfFileId}`;
console.log('PDF created successfully with public access:', pdfUrl);
// Clean up the temporary document (not the PDF file)
console.log('Cleaning up temporary document...');
DriveApp.getFileById(docId).setTrashed(true);
console.log('Temporary document deleted');
return {
success: true,
pdfUrl: pdfUrl,
pdfFileId: pdfFileId,
fileName: fileName
};
} catch (error) {
console.error('Error generating PDF:', error);
return {
success: false,
error: error.message
};
}
}
/**
* Check rate limiting
*/
function checkRateLimit() {
const now = Date.now();
const windowStart = now - CONFIG.RATE_LIMIT_WINDOW;
// Get current request count
const requestCount = parseInt(rateLimitStore.getProperty('requestCount') || '0');
const lastReset = parseInt(rateLimitStore.getProperty('lastReset') || '0');
// Reset if window has passed
if (now - lastReset > CONFIG.RATE_LIMIT_WINDOW) {
rateLimitStore.setProperties({
'requestCount': '1',
'lastReset': now.toString()
});
return true;
}
// Check if under limit
if (requestCount < CONFIG.MAX_REQUESTS_PER_WINDOW) {
rateLimitStore.setProperty('requestCount', (requestCount + 1).toString());
return true;
}
return false;
}
/**
* Create standardized response
*/
function createResponse(success, pdfUrl, error, fileName) {
const response = {
success: success,
timestamp: new Date().toISOString()
};
if (success && pdfUrl) {
response.pdfUrl = pdfUrl;
if (fileName) {
response.fileName = fileName;
}
} else if (error) {
response.error = error;
}
console.log('Response:', JSON.stringify(response, null, 2));
return ContentService
.createTextOutput(JSON.stringify(response))
.setMimeType(ContentService.MimeType.JSON);
}
/**
* 🧪 TEST FUNCTION - Run this from Apps Script editor (NOT generateStrengthFinderPDF)
* This function provides mock data and tests the PDF generation logic.
*/
function testPDFGeneration() {
console.log('🧪 Testing PDF generation...');
// Mock request data for testing
const testRequestData = {
assessmentId: 'test-123',
userName: 'Test User',
placeholders: {
user_name: 'Test User',
user_category: 'Working Professional',
current_date: '28 Oct, 2025 - 04:30 PM',
principle_percentage: '69',
ei_percentage: '80',
communication_percentage: '77',
problemsolving_percentage: '74',
ownership_percentage: '70',
collaboration_percentage: '71',
learning_percentage: '41',
habits_percentage: '48',
strong_trait1: 'Overtalker',
strong_trait1_score: '100',
strong_trait2: 'Empath',
strong_trait2_score: '88',
strong_trait3: 'Blamer',
strong_trait3_score: '84',
strong_trait4: 'Overthinker',
strong_trait4_score: '82',
strong_trait5: 'Harmonizer',
strong_trait5_score: '80',
weak_trait1: 'Stagnant',
weak_trait1_score: '20',
weak_trait2: 'Planner',
weak_trait2_score: '20',
weak_trait3: 'Practice',
weak_trait3_score: '33',
action_plan_1: 'Schedule regular team check-ins to discuss project progress',
action_plan_2: 'Practice active listening during team meetings',
action_plan_3: 'Document your contributions to group projects clearly',
action_plan_4: 'Seek feedback from colleagues on your collaboration style',
action_plan_5: 'Take initiative in leading small group discussions',
tip1: 'Develop strong active listening skills through focused practice',
tip2: 'Seek opportunities to lead collaborative projects at work',
tip3: 'Study conflict resolution techniques for better team dynamics',
tip4: 'Mentor junior colleagues on effective teamwork',
tip5: 'Build professional networks through industry events',
principle_explaination: 'You show respect for others\' ideas and maintain fairness in interactions',
ei_explaination: 'You understand others\' feelings well and show good empathy',
communication_explaination: 'Your communication shines through in team settings',
problemsolving_explaination: 'You approach challenges with practical solutions',
ownership_explaination: 'You take responsibility for team outcomes',
collaboration_explaination: 'You excel at working with others toward shared goals',
learning_explaination: 'You are open to learning from peers and adapting',
habits_explaination: 'You maintain consistent teamwork habits'
}
};
try {
const result = generateStrengthFinderPDF(testRequestData);
console.log('✅ Test result:', result);
return result;
} catch (error) {
console.error('❌ Test failed:', error);
return { success: false, error: error.message };
}
}