updating generation code to straight JS

This commit is contained in:
2026-01-27 12:16:28 -06:00
parent 451cc73864
commit 44f1176073
16 changed files with 896 additions and 896 deletions

382
generate_resume.js Normal file
View File

@@ -0,0 +1,382 @@
const fs = require('fs');
const {
Document, Packer, Paragraph, TextRun, AlignmentType,
HeadingLevel, LevelFormat, ExternalHyperlink, BorderStyle
} = require('docx');
// ============================================
// CONFIGURATION - Modify these paths as needed
// ============================================
const INPUT_FILE = './resume.json';
const OUTPUT_FILE = `./Phillip_Tarrant_Resume_${new Date().getFullYear()}.docx`;
// Load resume data
const resumeData = JSON.parse(fs.readFileSync(INPUT_FILE, 'utf8'));
// ============================================
// HELPER FUNCTIONS
// ============================================
// Create a bullet point paragraph
function createBulletParagraph(text, reference) {
return new Paragraph({
numbering: { reference: reference, level: 0 },
spacing: { after: 60 },
children: [new TextRun({ text: text, size: 22, font: "Arial" })]
});
}
// Format date from YYYY-MM to "Mon YYYY"
function formatDate(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr + '-01');
return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' });
}
// ============================================
// BUILD DOCUMENT CONTENT
// ============================================
const children = [];
// --- HEADER SECTION ---
// Name - centered, bold, larger
children.push(new Paragraph({
alignment: AlignmentType.CENTER,
spacing: { after: 80 },
children: [new TextRun({
text: resumeData.basics.name,
bold: true,
size: 32, // 16pt
font: "Arial"
})]
}));
// Title/Label - centered
children.push(new Paragraph({
alignment: AlignmentType.CENTER,
spacing: { after: 80 },
children: [new TextRun({
text: resumeData.basics.label,
size: 24, // 12pt
font: "Arial"
})]
}));
// Contact line 1: email • phone
children.push(new Paragraph({
alignment: AlignmentType.CENTER,
spacing: { after: 40 },
children: [new TextRun({
text: `${resumeData.basics.email}${resumeData.basics.phone}`,
size: 22, // 11pt
font: "Arial"
})]
}));
// Contact line 2: LinkedIn • Location
const location = `${resumeData.basics.location.city}, ${resumeData.basics.location.region}`;
children.push(new Paragraph({
alignment: AlignmentType.CENTER,
spacing: { after: 200 },
children: [
new ExternalHyperlink({
link: resumeData.basics.url,
children: [new TextRun({
text: resumeData.basics.url.replace('https://', ''),
style: "Hyperlink",
size: 22,
font: "Arial"
})]
}),
new TextRun({
text: `${location}`,
size: 22,
font: "Arial"
})
]
}));
// --- SUMMARY SECTION ---
children.push(new Paragraph({
spacing: { after: 240 },
children: [new TextRun({
text: resumeData.basics.summary,
size: 22,
font: "Arial"
})]
}));
// --- CAREER EXPERIENCE SECTION ---
children.push(new Paragraph({
spacing: { before: 120, after: 200 },
children: [new TextRun({
text: "Career Experience",
bold: true,
size: 26, // 13pt
font: "Arial"
})]
}));
// Work experience entries
resumeData.work.forEach((job, index) => {
const startDate = formatDate(job.startDate);
const endDate = job.endDate ? formatDate(job.endDate) : 'Present';
const dateRange = `${startDate} ${endDate}`;
const companyLine = job.location ? `${job.name}, ${job.location}` : job.name;
// Job title and company
children.push(new Paragraph({
spacing: { before: 160, after: 60 },
children: [
new TextRun({
text: job.position,
bold: true,
size: 22,
font: "Arial"
}),
new TextRun({
text: `\n${companyLine}`,
size: 22,
font: "Arial"
}),
new TextRun({
text: ` ${dateRange}`,
size: 22,
font: "Arial",
italics: true
})
]
}));
// Job summary
if (job.summary) {
children.push(new Paragraph({
spacing: { after: 100 },
children: [new TextRun({
text: job.summary,
size: 22,
font: "Arial"
})]
}));
}
// Highlights as bullet points
if (job.highlights && job.highlights.length > 0) {
job.highlights.forEach(highlight => {
children.push(createBulletParagraph(highlight, "bullets"));
});
}
// Spacing after each job
children.push(new Paragraph({ spacing: { after: 120 }, children: [] }));
});
// --- EDUCATION, CERTIFICATIONS, AND AWARDS SECTION ---
children.push(new Paragraph({
spacing: { before: 200, after: 160 },
children: [new TextRun({
text: "Education, Certifications, and Awards",
bold: true,
size: 26,
font: "Arial"
})]
}));
// Certificates
if (resumeData.certificates) {
resumeData.certificates.forEach(cert => {
const year = cert.date ? new Date(cert.date).getFullYear() : '';
children.push(new Paragraph({
spacing: { after: 60 },
children: [
new TextRun({
text: cert.name,
bold: true,
size: 22,
font: "Arial"
}),
new TextRun({
text: `, ${cert.issuer}`,
size: 22,
font: "Arial"
}),
new TextRun({
text: year ? `, ${year}` : '',
size: 22,
font: "Arial"
})
]
}));
});
}
// Awards
if (resumeData.awards) {
resumeData.awards.forEach(award => {
const year = award.date ? new Date(award.date).getFullYear() : '';
children.push(new Paragraph({
spacing: { after: 60 },
children: [
new TextRun({
text: award.title,
bold: true,
size: 22,
font: "Arial"
}),
new TextRun({
text: `, ${award.awarder}`,
size: 22,
font: "Arial"
}),
new TextRun({
text: year ? `, ${year}` : '',
size: 22,
font: "Arial"
})
]
}));
if (award.summary) {
children.push(new Paragraph({
spacing: { after: 60 },
indent: { left: 360 },
children: [new TextRun({
text: award.summary,
size: 22,
font: "Arial",
italics: true
})]
}));
}
});
}
// Education
if (resumeData.education) {
resumeData.education.forEach(edu => {
const dateRange = edu.startDate && edu.endDate ? `${edu.startDate} ${edu.endDate}` : '';
children.push(new Paragraph({
spacing: { after: 60 },
children: [
new TextRun({
text: `${edu.studyType} in ${edu.area}`,
bold: true,
size: 22,
font: "Arial"
}),
new TextRun({
text: `, ${edu.institution}`,
size: 22,
font: "Arial"
}),
new TextRun({
text: dateRange ? `, ${dateRange}` : '',
size: 22,
font: "Arial"
})
]
}));
if (edu.score) {
children.push(new Paragraph({
spacing: { after: 60 },
indent: { left: 360 },
children: [new TextRun({
text: `GPA: ${edu.score}`,
size: 22,
font: "Arial",
italics: true
})]
}));
}
});
}
// --- TECHNICAL SKILLS SECTION ---
children.push(new Paragraph({
spacing: { before: 200, after: 160 },
children: [new TextRun({
text: "Technical Skills",
bold: true,
size: 26,
font: "Arial"
})]
}));
if (resumeData.skills) {
resumeData.skills.forEach(skillCategory => {
children.push(new Paragraph({
spacing: { after: 60 },
children: [
new TextRun({
text: `${skillCategory.name}: `,
bold: true,
size: 22,
font: "Arial"
}),
new TextRun({
text: skillCategory.keywords.join(", "),
size: 22,
font: "Arial"
})
]
}));
});
}
// ============================================
// CREATE AND SAVE DOCUMENT
// ============================================
const doc = new Document({
styles: {
default: {
document: {
run: { font: "Arial", size: 22 }
}
}
},
numbering: {
config: [
{
reference: "bullets",
levels: [{
level: 0,
format: LevelFormat.BULLET,
text: "•",
alignment: AlignmentType.LEFT,
style: {
paragraph: {
indent: { left: 720, hanging: 360 }
}
}
}]
}
]
},
sections: [{
properties: {
page: {
size: {
width: 12240, // 8.5 inches (1440 DXA = 1 inch)
height: 15840 // 11 inches
},
margin: {
top: 720, // 0.5 inch
right: 1080, // 0.75 inch
bottom: 720, // 0.5 inch
left: 1080 // 0.75 inch
}
}
},
children: children
}]
});
// Save the document
Packer.toBuffer(doc).then(buffer => {
fs.writeFileSync(OUTPUT_FILE, buffer);
console.log(`Resume created successfully: ${OUTPUT_FILE}`);
}).catch(err => {
console.error('Error creating document:', err);
process.exit(1);
});