feat: ATS-safe CV rework (EN + NO), replacing scrambled two-column PDFs
- single-column, real-text PDFs: standard headings (Summary, Skills, Experience, Projects, Education), consistent dates, no letter-spacing - 'Systems Developer, eight years experience' (drops 'mid-level'); apprentice->developer progression shown; Projects section added; Norwegian written natively (errors fixed) - one page each, ~8 kB (was 132/172 kB); verified clean ATS text extraction + reading order - generator committed to tools/cv (content.js + pdfkit/docx builders) for reproducibility - editable .docx kept alongside; site file-size labels updated Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
# Binary assets — never touch.
|
||||
*.docx binary
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -15,8 +15,8 @@ export const profile: Profile = {
|
||||
gitea: 'https://git.cesnimda.uk/cesnimda',
|
||||
},
|
||||
cv: {
|
||||
en: { path: '/cv/connor-babbington-cv-en.pdf', sizeKb: 132, updated: '2026-07' },
|
||||
no: { path: '/cv/connor-babbington-cv-no.pdf', sizeKb: 172, updated: '2026-07' },
|
||||
en: { path: '/cv/connor-babbington-cv-en.pdf', sizeKb: 8, updated: '2026-07' },
|
||||
no: { path: '/cv/connor-babbington-cv-no.pdf', sizeKb: 8, updated: '2026-07' },
|
||||
},
|
||||
photo: {
|
||||
src: 'placeholder:portrait',
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,28 @@
|
||||
# CV generator
|
||||
|
||||
Produces Connor Babbington's CV in both languages as **ATS-safe PDFs** (single column,
|
||||
real text, standard headings, no letter-spacing tricks) and as editable **.docx**.
|
||||
|
||||
Content lives in [`content.js`](content.js) — the single source, mirroring
|
||||
`docs/design/02-ux/CANONICAL_CONTENT.md`. Positioning is "Systems Developer, eight
|
||||
years' experience" (not "mid-level"); the council role shows the apprentice→developer
|
||||
progression; a Projects section is included; Norwegian is written natively.
|
||||
|
||||
## Regenerate
|
||||
|
||||
```bash
|
||||
cd tools/cv
|
||||
npm install
|
||||
npm run pdf # -> connor-babbington-cv-en.pdf / -no.pdf (deploy these to site/public/cv/)
|
||||
npm run docx # -> Connor-Babbington-CV-EN.docx / -NO.docx (editable source)
|
||||
```
|
||||
|
||||
Then copy the PDFs into `site/public/cv/` (same filenames) — the site links to those
|
||||
stable URLs, so sharing links always fetch the newest CV.
|
||||
|
||||
## Notes
|
||||
|
||||
- The download default on the site is the **ATS-safe PDF** (these files).
|
||||
- The current PDFs replaced the original two-column, letter-spaced CVs, which parsed
|
||||
as scrambled text in applicant-tracking systems.
|
||||
- `.docx` files are kept for easy manual editing; re-export or regenerate after edits.
|
||||
@@ -0,0 +1,216 @@
|
||||
const fs = require('fs');
|
||||
const {
|
||||
Document, Packer, Paragraph, TextRun, ExternalHyperlink,
|
||||
AlignmentType, LevelFormat, TabStopType, BorderStyle, HeadingLevel,
|
||||
} = require('docx');
|
||||
|
||||
// A4, 1 inch margins. Content width = 11906 - 2*1440 = 9026 DXA.
|
||||
const CONTENT_W = 9026;
|
||||
|
||||
function styles() {
|
||||
return {
|
||||
default: { document: { run: { font: 'Arial', size: 21 } } }, // 10.5pt
|
||||
paragraphStyles: [
|
||||
{ id: 'Heading2', name: 'Heading 2', basedOn: 'Normal', next: 'Normal', quickFormat: true,
|
||||
run: { size: 24, bold: true, font: 'Arial', color: '1A1A1A' },
|
||||
paragraph: {
|
||||
spacing: { before: 280, after: 120 }, outlineLevel: 1,
|
||||
border: { bottom: { style: BorderStyle.SINGLE, size: 6, color: 'B0B0B0', space: 4 } },
|
||||
} },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const numbering = {
|
||||
config: [{
|
||||
reference: 'b',
|
||||
levels: [{ level: 0, format: LevelFormat.BULLET, text: '•', alignment: AlignmentType.LEFT,
|
||||
style: { paragraph: { indent: { left: 360, hanging: 220 } } } }],
|
||||
}],
|
||||
};
|
||||
|
||||
const bullet = (text) =>
|
||||
new Paragraph({ numbering: { reference: 'b', level: 0 }, spacing: { after: 40 },
|
||||
children: [new TextRun(text)] });
|
||||
|
||||
const heading = (text) => new Paragraph({ heading: HeadingLevel.HEADING_2, children: [new TextRun(text)] });
|
||||
|
||||
// Role line: "Role — Org" left, dates right (tab stop).
|
||||
const roleLine = (left, dates) =>
|
||||
new Paragraph({
|
||||
spacing: { before: 120, after: 0 },
|
||||
tabStops: [{ type: TabStopType.RIGHT, position: CONTENT_W }],
|
||||
children: [new TextRun({ text: left, bold: true }), new TextRun({ text: `\t${dates}`, color: '555555' })],
|
||||
});
|
||||
|
||||
const note = (text) =>
|
||||
new Paragraph({ spacing: { after: 40 }, children: [new TextRun({ text, italics: true, color: '555555' })] });
|
||||
|
||||
const skillLine = (group, items) =>
|
||||
new Paragraph({ spacing: { after: 40 },
|
||||
children: [new TextRun({ text: `${group}: `, bold: true }), new TextRun(items)] });
|
||||
|
||||
const projectLine = (name, desc) =>
|
||||
new Paragraph({ numbering: { reference: 'b', level: 0 }, spacing: { after: 40 },
|
||||
children: [new TextRun({ text: `${name} — `, bold: true }), new TextRun(desc)] });
|
||||
|
||||
function buildCv(c) {
|
||||
const children = [];
|
||||
|
||||
// Name + title
|
||||
children.push(new Paragraph({ spacing: { after: 0 },
|
||||
children: [new TextRun({ text: c.name, bold: true, size: 40 })] }));
|
||||
children.push(new Paragraph({ spacing: { after: 60 },
|
||||
children: [new TextRun({ text: c.title, size: 24, color: '333333' })] }));
|
||||
|
||||
// Contact (with hyperlinks), rule beneath
|
||||
children.push(new Paragraph({
|
||||
spacing: { after: 20 },
|
||||
children: [
|
||||
new TextRun(`${c.location} · `),
|
||||
new ExternalHyperlink({ link: `mailto:${c.email}`, children: [new TextRun({ text: c.email, style: 'Hyperlink' })] }),
|
||||
new TextRun(` · ${c.phone}`),
|
||||
],
|
||||
}));
|
||||
children.push(new Paragraph({
|
||||
spacing: { after: 40 },
|
||||
border: { bottom: { style: BorderStyle.SINGLE, size: 6, color: 'B0B0B0', space: 6 } },
|
||||
children: [
|
||||
new ExternalHyperlink({ link: 'https://cesnimda.co.uk', children: [new TextRun({ text: 'cesnimda.co.uk', style: 'Hyperlink' })] }),
|
||||
new TextRun(' · '),
|
||||
new ExternalHyperlink({ link: 'https://cesnimda.co.uk/Linkedin', children: [new TextRun({ text: 'LinkedIn', style: 'Hyperlink' })] }),
|
||||
],
|
||||
}));
|
||||
|
||||
// Summary
|
||||
children.push(heading(c.t.summary));
|
||||
children.push(new Paragraph({ spacing: { after: 40 }, children: [new TextRun(c.summary)] }));
|
||||
|
||||
// Skills
|
||||
children.push(heading(c.t.skills));
|
||||
for (const s of c.skills) children.push(skillLine(s.group, s.items));
|
||||
|
||||
// Experience
|
||||
children.push(heading(c.t.experience));
|
||||
for (const e of c.experience) {
|
||||
children.push(roleLine(`${e.role} — ${e.org}`, e.dates));
|
||||
if (e.note) children.push(note(e.note));
|
||||
for (const b of e.bullets) children.push(bullet(b));
|
||||
}
|
||||
|
||||
// Earlier roles
|
||||
children.push(new Paragraph({ spacing: { before: 100, after: 20 },
|
||||
children: [new TextRun({ text: c.t.earlier, bold: true })] }));
|
||||
for (const r of c.earlier) children.push(bullet(r));
|
||||
|
||||
// Projects
|
||||
children.push(heading(c.t.projects));
|
||||
for (const p of c.projects) children.push(projectLine(p.name, p.desc));
|
||||
|
||||
// Education
|
||||
children.push(heading(c.t.education));
|
||||
children.push(roleLine(c.education.qual, c.education.dates));
|
||||
children.push(new Paragraph({ children: [new TextRun(c.education.org)] }));
|
||||
|
||||
return new Document({
|
||||
styles: styles(),
|
||||
numbering,
|
||||
sections: [{
|
||||
properties: { page: { size: { width: 11906, height: 16838 }, margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } } },
|
||||
children,
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
const EN = {
|
||||
name: 'Connor Babbington',
|
||||
title: 'Systems Developer',
|
||||
location: 'Tønsberg, Norway',
|
||||
email: 'connor.babbington@cesnimda.co.uk',
|
||||
phone: '+47 41 33 44 70',
|
||||
t: { summary: 'Professional Summary', skills: 'Core Skills', experience: 'Experience',
|
||||
earlier: 'Earlier roles (part-time, alongside the above)', projects: 'Projects', education: 'Education' },
|
||||
summary:
|
||||
"Systems developer with eight years' experience building and maintaining production software for UK local government. A backend-leaning full-stack developer across C#, .NET, Python, JavaScript/TypeScript and SQL, with hands-on DevOps in Docker, Linux, CI/CD, Azure DevOps and GitHub. I turn stakeholder requirements into reliable, well-tested systems and support them in production. Based in Tønsberg with a valid residence permit. Native English speaker; Norwegian at B1 and actively developing. Open to remote, hybrid or on-site roles.",
|
||||
skills: [
|
||||
{ group: 'Development', items: 'C#, .NET, Python, JavaScript, TypeScript, React, SQL' },
|
||||
{ group: 'DevOps & Infrastructure', items: 'Docker, Linux, CI/CD, Azure DevOps, GitHub, nginx / reverse proxies, monitoring, self-hosting' },
|
||||
{ group: 'Practices', items: 'Testing, security hardening, OAuth2 integrations, production support, stakeholder communication' },
|
||||
],
|
||||
experience: [
|
||||
{ role: 'System Developer', org: 'Warwickshire County Council, UK', dates: '2015–2023',
|
||||
note: 'The first two years were completed as an apprenticeship.',
|
||||
bullets: [
|
||||
'Worked on the county-wide highways and streetlight fault-reporting system used across Warwickshire.',
|
||||
'Designed, built and maintained full-stack applications in C#, Python, Ruby on Rails, SQL and JavaScript.',
|
||||
'Delivered reliable, well-tested software and resolved stability and performance issues on live systems.',
|
||||
'Owned deployments and production troubleshooting, and strengthened permissions and reliability.',
|
||||
'Guided colleagues on best practices for code and process.',
|
||||
] },
|
||||
{ role: 'Independent Development & Norwegian Study', org: 'Tønsberg, Norway', dates: '2023–Present',
|
||||
bullets: [
|
||||
'Building and self-hosting full-stack products (see Projects) while developing Norwegian language skills.',
|
||||
] },
|
||||
],
|
||||
earlier: [
|
||||
'Sales Representative — Royal Vapes, UK (2017–2021)',
|
||||
'Bartender — The Hodcarrier, UK (2016–2018)',
|
||||
'Receptionist — Nuffield Health, UK (2014–2015)',
|
||||
],
|
||||
projects: [
|
||||
{ name: 'JobTrack', desc: 'Full-stack job-application tracker (React, ASP.NET Core, SQLite, Docker) with a local AI summariser and Gmail integration.' },
|
||||
{ name: 'InboxIntel', desc: 'Gmail analytics and safe bulk-cleanup tool built as Clean Architecture in .NET 8 with PostgreSQL and encrypted OAuth tokens.' },
|
||||
{ name: 'Self-hosted infrastructure lab', desc: 'Ubuntu, Docker, nginx reverse proxy, authentication, monitoring and a self-hosted Gitea instance.' },
|
||||
],
|
||||
education: { qual: 'Extended Diploma — NVQ Level 3 in ICT', org: 'Warwickshire College, UK', dates: '2012–2015' },
|
||||
};
|
||||
|
||||
const NO = {
|
||||
name: 'Connor Babbington',
|
||||
title: 'Systemutvikler',
|
||||
location: 'Tønsberg',
|
||||
email: 'connor.babbington@cesnimda.co.uk',
|
||||
phone: '+47 41 33 44 70',
|
||||
t: { summary: 'Sammendrag', skills: 'Kjernekompetanse', experience: 'Erfaring',
|
||||
earlier: 'Tidligere roller (deltid, ved siden av)', projects: 'Prosjekter', education: 'Utdanning' },
|
||||
summary:
|
||||
'Systemutvikler med åtte års erfaring med å bygge og vedlikeholde produksjonssystemer i britisk offentlig sektor. Fullstack-utvikler med tyngde på backend i C#, .NET, Python, JavaScript/TypeScript og SQL, med praktisk DevOps i Docker, Linux, CI/CD, Azure DevOps og GitHub. Jeg omsetter behov fra brukere og interessenter til pålitelige, godt testede systemer og drifter dem i produksjon. Bosatt i Tønsberg med gyldig oppholdstillatelse. Engelsk morsmål; norsk på B1-nivå og i aktiv utvikling. Åpen for remote, hybrid eller stedbaserte roller.',
|
||||
skills: [
|
||||
{ group: 'Utvikling', items: 'C#, .NET, Python, JavaScript, TypeScript, React, SQL' },
|
||||
{ group: 'DevOps og infrastruktur', items: 'Docker, Linux, CI/CD, Azure DevOps, GitHub, nginx / reverse proxy, overvåking, egendrift' },
|
||||
{ group: 'Arbeidsmåte', items: 'Testing, sikkerhet, OAuth2-integrasjoner, driftsstøtte, kommunikasjon med interessenter' },
|
||||
],
|
||||
experience: [
|
||||
{ role: 'Systemutvikler', org: 'Warwickshire County Council, UK', dates: '2015–2023',
|
||||
note: 'De to første årene var en lærlingperiode.',
|
||||
bullets: [
|
||||
'Jobbet på det fylkesdekkende systemet for feilmelding av veilys og veier, brukt i hele Warwickshire.',
|
||||
'Designet, bygde og vedlikeholdt fullstack-applikasjoner i C#, Python, Ruby on Rails, SQL og JavaScript.',
|
||||
'Leverte pålitelig, godt testet programvare og løste stabilitets- og ytelsesproblemer i produksjon.',
|
||||
'Hadde ansvar for utrulling og feilsøking i drift, og styrket tilganger og pålitelighet.',
|
||||
'Veiledet kollegaer i beste praksis for kode og prosess.',
|
||||
] },
|
||||
{ role: 'Egen produktutvikling og norskkurs', org: 'Tønsberg', dates: '2023–nå',
|
||||
bullets: [
|
||||
'Bygger og drifter egne fullstack-produkter (se Prosjekter) samtidig som jeg utvikler norskkunnskapene.',
|
||||
] },
|
||||
],
|
||||
earlier: [
|
||||
'Salgsrepresentant — Royal Vapes, UK (2017–2021)',
|
||||
'Bartender — The Hodcarrier, UK (2016–2018)',
|
||||
'Resepsjonist — Nuffield Health, UK (2014–2015)',
|
||||
],
|
||||
projects: [
|
||||
{ name: 'JobTrack', desc: 'Fullstack jobbsøknads-tracker (React, ASP.NET Core, SQLite, Docker) med lokal AI-oppsummering og Gmail-integrasjon.' },
|
||||
{ name: 'InboxIntel', desc: 'Verktøy for Gmail-analyse og trygg masseopprydding, bygget som Clean Architecture i .NET 8 med PostgreSQL og krypterte OAuth-tokens.' },
|
||||
{ name: 'Egendriftet hjemmelab', desc: 'Ubuntu, Docker, nginx reverse proxy, autentisering, overvåking og egen Gitea-instans.' },
|
||||
],
|
||||
education: { qual: 'Extended Diploma — NVQ nivå 3 i IKT', org: 'Warwickshire College, UK', dates: '2012–2015' },
|
||||
};
|
||||
|
||||
async function main() {
|
||||
await Packer.toBuffer(buildCv(EN)).then((b) => fs.writeFileSync('Connor-Babbington-CV-EN.docx', b));
|
||||
await Packer.toBuffer(buildCv(NO)).then((b) => fs.writeFileSync('Connor-Babbington-CV-NO.docx', b));
|
||||
console.log('wrote EN + NO docx');
|
||||
}
|
||||
main();
|
||||
@@ -0,0 +1,107 @@
|
||||
const fs = require('fs');
|
||||
const PDFDocument = require('pdfkit');
|
||||
|
||||
// Shared content (mirrors build-cv.js).
|
||||
const { EN, NO } = require('./content.js');
|
||||
|
||||
const PAGE_W = 595.28;
|
||||
const MARGIN = 54;
|
||||
const CW = PAGE_W - MARGIN * 2; // content width
|
||||
const INK = '#1a1a1a';
|
||||
const GRAY = '#555555';
|
||||
const RULE = '#b0b0b0';
|
||||
|
||||
function build(c, outfile) {
|
||||
const doc = new PDFDocument({ size: 'A4', margins: { top: MARGIN, bottom: MARGIN, left: MARGIN, right: MARGIN } });
|
||||
doc.pipe(fs.createWriteStream(outfile));
|
||||
|
||||
const rule = (gap = 4) => {
|
||||
const y = doc.y + gap;
|
||||
doc.moveTo(MARGIN, y).lineTo(MARGIN + CW, y).lineWidth(0.75).strokeColor(RULE).stroke();
|
||||
doc.y = y + gap + 2;
|
||||
};
|
||||
|
||||
const heading = (text) => {
|
||||
doc.moveDown(0.5);
|
||||
doc.fillColor(INK).font('Helvetica-Bold').fontSize(12).text(text, MARGIN, doc.y);
|
||||
rule(2);
|
||||
};
|
||||
|
||||
const bullet = (text, prefix) => {
|
||||
const y = doc.y;
|
||||
doc.fillColor(INK).font('Helvetica').fontSize(10).text('•', MARGIN + 2, y, { width: 10, lineBreak: false });
|
||||
const tx = MARGIN + 16;
|
||||
const tw = CW - 16;
|
||||
if (prefix) {
|
||||
doc.font('Helvetica-Bold').text(prefix, tx, y, { width: tw, continued: true });
|
||||
doc.font('Helvetica').text(text, { width: tw });
|
||||
} else {
|
||||
doc.font('Helvetica').text(text, tx, y, { width: tw });
|
||||
}
|
||||
doc.moveDown(0.15);
|
||||
};
|
||||
|
||||
const roleLine = (left, dates) => {
|
||||
doc.moveDown(0.3);
|
||||
const y = doc.y;
|
||||
doc.font('Helvetica').fontSize(9.5).fillColor(GRAY).text(dates, MARGIN, y, { width: CW, align: 'right', lineBreak: false });
|
||||
doc.font('Helvetica-Bold').fontSize(10.5).fillColor(INK).text(left, MARGIN, y, { width: CW - 90, lineBreak: false });
|
||||
doc.y = y + 14;
|
||||
};
|
||||
|
||||
// Header
|
||||
doc.fillColor(INK).font('Helvetica-Bold').fontSize(20).text(c.name, MARGIN, MARGIN);
|
||||
doc.font('Helvetica').fontSize(12).fillColor('#333').text(c.title, { paragraphGap: 2 });
|
||||
doc.font('Helvetica').fontSize(9.5).fillColor(GRAY)
|
||||
.text(`${c.location} · ${c.email} · ${c.phone}`);
|
||||
doc.text('cesnimda.co.uk · cesnimda.co.uk/Linkedin');
|
||||
rule(4);
|
||||
|
||||
// Summary
|
||||
heading(c.t.summary);
|
||||
doc.font('Helvetica').fontSize(10).fillColor(INK).text(c.summary, MARGIN, doc.y, { width: CW, align: 'justify' });
|
||||
|
||||
// Skills
|
||||
heading(c.t.skills);
|
||||
for (const s of c.skills) {
|
||||
const y = doc.y;
|
||||
doc.font('Helvetica-Bold').fontSize(10).fillColor(INK).text(`${s.group}: `, MARGIN, y, { width: CW, continued: true });
|
||||
doc.font('Helvetica').text(s.items, { width: CW });
|
||||
doc.moveDown(0.15);
|
||||
}
|
||||
|
||||
// Experience
|
||||
heading(c.t.experience);
|
||||
for (const e of c.experience) {
|
||||
roleLine(`${e.role} — ${e.org}`, e.dates);
|
||||
if (e.note) {
|
||||
doc.font('Helvetica-Oblique').fontSize(9.5).fillColor(GRAY).text(e.note, MARGIN, doc.y, { width: CW });
|
||||
doc.moveDown(0.1);
|
||||
}
|
||||
for (const b of e.bullets) bullet(b);
|
||||
}
|
||||
|
||||
// Earlier roles
|
||||
doc.moveDown(0.4);
|
||||
doc.font('Helvetica-Bold').fontSize(10).fillColor(INK).text(c.t.earlier, MARGIN, doc.y, { width: CW });
|
||||
doc.moveDown(0.1);
|
||||
for (const r of c.earlier) bullet(r);
|
||||
|
||||
// Projects
|
||||
heading(c.t.projects);
|
||||
for (const p of c.projects) bullet(p.desc, `${p.name} — `);
|
||||
|
||||
// Education
|
||||
heading(c.t.education);
|
||||
roleLine(c.education.qual, c.education.dates);
|
||||
doc.font('Helvetica').fontSize(10).fillColor(INK).text(c.education.org, MARGIN, doc.y, { width: CW });
|
||||
|
||||
doc.end();
|
||||
return new Promise((res) => doc.on('end', res));
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await build(EN, 'connor-babbington-cv-en.pdf');
|
||||
await build(NO, 'connor-babbington-cv-no.pdf');
|
||||
console.log('wrote EN + NO pdf');
|
||||
})();
|
||||
@@ -0,0 +1,87 @@
|
||||
const EN = {
|
||||
name: 'Connor Babbington',
|
||||
title: 'Systems Developer',
|
||||
location: 'Tønsberg, Norway',
|
||||
email: 'connor.babbington@cesnimda.co.uk',
|
||||
phone: '+47 41 33 44 70',
|
||||
t: { summary: 'Professional Summary', skills: 'Core Skills', experience: 'Experience',
|
||||
earlier: 'Earlier roles (part-time, alongside the above)', projects: 'Projects', education: 'Education' },
|
||||
summary:
|
||||
"Systems developer with eight years’ experience building and maintaining production software for UK local government. A backend-leaning full-stack developer across C#, .NET, Python, JavaScript/TypeScript and SQL, with hands-on DevOps in Docker, Linux, CI/CD, Azure DevOps and GitHub. I turn stakeholder requirements into reliable, well-tested systems and support them in production. Based in Tønsberg with a valid residence permit. Native English speaker; Norwegian at B1 and actively developing. Open to remote, hybrid or on-site roles.",
|
||||
skills: [
|
||||
{ group: 'Development', items: 'C#, .NET, Python, JavaScript, TypeScript, React, SQL' },
|
||||
{ group: 'DevOps & Infrastructure', items: 'Docker, Linux, CI/CD, Azure DevOps, GitHub, nginx / reverse proxies, monitoring, self-hosting' },
|
||||
{ group: 'Practices', items: 'Testing, security hardening, OAuth2 integrations, production support, stakeholder communication' },
|
||||
],
|
||||
experience: [
|
||||
{ role: 'System Developer', org: 'Warwickshire County Council, UK', dates: '2015–2023',
|
||||
note: 'The first two years were completed as an apprenticeship.',
|
||||
bullets: [
|
||||
'Worked on the county-wide highways and streetlight fault-reporting system used across Warwickshire.',
|
||||
'Designed, built and maintained full-stack applications in C#, Python, Ruby on Rails, SQL and JavaScript.',
|
||||
'Delivered reliable, well-tested software and resolved stability and performance issues on live systems.',
|
||||
'Owned deployments and production troubleshooting, and strengthened permissions and reliability.',
|
||||
'Guided colleagues on best practices for code and process.',
|
||||
] },
|
||||
{ role: 'Independent Development & Norwegian Study', org: 'Tønsberg, Norway', dates: '2023–Present',
|
||||
bullets: [
|
||||
'Building and self-hosting full-stack products (see Projects) while developing Norwegian language skills.',
|
||||
] },
|
||||
],
|
||||
earlier: [
|
||||
'Sales Representative — Royal Vapes, UK (2017–2021)',
|
||||
'Bartender — The Hodcarrier, UK (2016–2018)',
|
||||
'Receptionist — Nuffield Health, UK (2014–2015)',
|
||||
],
|
||||
projects: [
|
||||
{ name: 'JobTrack', desc: 'Full-stack job-application tracker (React, ASP.NET Core, SQLite, Docker) with a local AI summariser and Gmail integration.' },
|
||||
{ name: 'InboxIntel', desc: 'Gmail analytics and safe bulk-cleanup tool built as Clean Architecture in .NET 8 with PostgreSQL and encrypted OAuth tokens.' },
|
||||
{ name: 'Self-hosted infrastructure lab', desc: 'Ubuntu, Docker, nginx reverse proxy, authentication, monitoring and a self-hosted Gitea instance.' },
|
||||
],
|
||||
education: { qual: 'Extended Diploma — NVQ Level 3 in ICT', org: 'Warwickshire College, UK', dates: '2012–2015' },
|
||||
};
|
||||
|
||||
const NO = {
|
||||
name: 'Connor Babbington',
|
||||
title: 'Systemutvikler',
|
||||
location: 'Tønsberg',
|
||||
email: 'connor.babbington@cesnimda.co.uk',
|
||||
phone: '+47 41 33 44 70',
|
||||
t: { summary: 'Sammendrag', skills: 'Kjernekompetanse', experience: 'Erfaring',
|
||||
earlier: 'Tidligere roller (deltid, ved siden av)', projects: 'Prosjekter', education: 'Utdanning' },
|
||||
summary:
|
||||
'Systemutvikler med åtte års erfaring med å bygge og vedlikeholde produksjonssystemer i britisk offentlig sektor. Fullstack-utvikler med tyngde på backend i C#, .NET, Python, JavaScript/TypeScript og SQL, med praktisk DevOps i Docker, Linux, CI/CD, Azure DevOps og GitHub. Jeg omsetter behov fra brukere og interessenter til pålitelige, godt testede systemer og drifter dem i produksjon. Bosatt i Tønsberg med gyldig oppholdstillatelse. Engelsk morsmål; norsk på B1-nivå og i aktiv utvikling. Åpen for remote, hybrid eller stedbaserte roller.',
|
||||
skills: [
|
||||
{ group: 'Utvikling', items: 'C#, .NET, Python, JavaScript, TypeScript, React, SQL' },
|
||||
{ group: 'DevOps og infrastruktur', items: 'Docker, Linux, CI/CD, Azure DevOps, GitHub, nginx / reverse proxy, overvåking, egendrift' },
|
||||
{ group: 'Arbeidsmåte', items: 'Testing, sikkerhet, OAuth2-integrasjoner, driftsstøtte, kommunikasjon med interessenter' },
|
||||
],
|
||||
experience: [
|
||||
{ role: 'Systemutvikler', org: 'Warwickshire County Council, UK', dates: '2015–2023',
|
||||
note: 'De to første årene var en lærlingperiode.',
|
||||
bullets: [
|
||||
'Jobbet på det fylkesdekkende systemet for feilmelding av veilys og veier, brukt i hele Warwickshire.',
|
||||
'Designet, bygde og vedlikeholdt fullstack-applikasjoner i C#, Python, Ruby on Rails, SQL og JavaScript.',
|
||||
'Leverte pålitelig, godt testet programvare og løste stabilitets- og ytelsesproblemer i produksjon.',
|
||||
'Hadde ansvar for utrulling og feilsøking i drift, og styrket tilganger og pålitelighet.',
|
||||
'Veiledet kollegaer i beste praksis for kode og prosess.',
|
||||
] },
|
||||
{ role: 'Egen produktutvikling og norskkurs', org: 'Tønsberg', dates: '2023–nå',
|
||||
bullets: [
|
||||
'Bygger og drifter egne fullstack-produkter (se Prosjekter) samtidig som jeg utvikler norskkunnskapene.',
|
||||
] },
|
||||
],
|
||||
earlier: [
|
||||
'Salgsrepresentant — Royal Vapes, UK (2017–2021)',
|
||||
'Bartender — The Hodcarrier, UK (2016–2018)',
|
||||
'Resepsjonist — Nuffield Health, UK (2014–2015)',
|
||||
],
|
||||
projects: [
|
||||
{ name: 'JobTrack', desc: 'Fullstack jobbsøknads-tracker (React, ASP.NET Core, SQLite, Docker) med lokal AI-oppsummering og Gmail-integrasjon.' },
|
||||
{ name: 'InboxIntel', desc: 'Verktøy for Gmail-analyse og trygg masseopprydding, bygget som Clean Architecture i .NET 8 med PostgreSQL og krypterte OAuth-tokens.' },
|
||||
{ name: 'Egendriftet hjemmelab', desc: 'Ubuntu, Docker, nginx reverse proxy, autentisering, overvåking og egen Gitea-instans.' },
|
||||
],
|
||||
education: { qual: 'Extended Diploma — NVQ nivå 3 i IKT', org: 'Warwickshire College, UK', dates: '2012–2015' },
|
||||
};
|
||||
|
||||
module.exports = { EN, NO };
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "resumesite-cv",
|
||||
"private": true,
|
||||
"description": "Generates the ATS-safe CV PDFs (and editable .docx) from canonical content",
|
||||
"scripts": {
|
||||
"pdf": "node build-pdf.js",
|
||||
"docx": "node build-cv.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"docx": "^9.0.0",
|
||||
"pdfkit": "^0.15.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user