Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to match the latest Resume.JSON format #66

Merged
merged 2 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 111 additions & 15 deletions src/js/converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ interface Output {
references?: object;
skills?: object;
work?: object;
volunteer?: object;
awards?: object;
certificates: object;
}

interface Position {
company: string;
name: string;
position: string;
website: string;
url: string;
startDate: string;
summary: string;
highlights: Array<string>;
Expand All @@ -25,10 +28,11 @@ interface Position {

interface Education {
institution: string;
url?: string;
area: string;
studyType: string;
startDate: string;
gpa: string;
score: string;
courses: Array<string>;
endDate?: string;
}
Expand All @@ -47,6 +51,7 @@ class LinkedInToJsonResume {
"volunteer",
"education",
"awards",
"certificates",
"publications",
"skills",
"languages",
Expand All @@ -72,27 +77,44 @@ class LinkedInToJsonResume {
processProfile(source) {
this.target.basics = this.target.basics || {};

const ccItem = CountryCodes.find((item) => item.name === source.country);
// Splitting the address into city, region and country
// and trimming the values
// example: "Dublin, Leinster, Ireland" => ["Dublin", "Leinster", "Ireland"]
const addressArray = source.geoLocation
.split(",")
.map((item) => item.trim());

const ccItem = CountryCodes.find(
(item) => item.name === addressArray[addressArray.length - 1]
);
let countryCode = "";
if (ccItem) {
countryCode = ccItem["alpha-2"];
}

// Extracting the URL from the LinkedIn profile
// this regex will match the first URL in the format [NETWORK:URL]
// This regex will remove the network part and extract the URL
// it applies for https and http URLs
// e.g. [Twitter:https://twitter.com/housamz] will return https://twitter.com/housamz
const websiteRegex = /\[([A-Z]+):(https?:\/\/[^\s,\]]+)/;

this._extend(this.target.basics, {
name: `${source.firstName} ${source.lastName}`,
label: source.headline,
picture: "",
image: "",
email: "",
website: source.websites
? source.websites.split(",")[0].split(":").slice(1).join(":")
phone: "",
url: source.websites
? source.websites.match(websiteRegex)[2]
: "",
summary: source.summary,
location: {
address: source.address,
postalCode: source.zipCode,
city: source.location ? source.location.name : "",
city: source.geoLocation ? addressArray[0] : "",
countryCode: countryCode,
region: "",
region: addressArray[1],
},
profiles: source.twitterHandles
? [
Expand All @@ -114,16 +136,20 @@ class LinkedInToJsonResume {
processPosition(source) {
function processPosition(position) {
let object = <Position>{
company: position.companyName,
name: position.companyName,
position: position.title || "",
website: "",
url: "",
startDate: `${position.startDate}`,
endDate: "",
summary: position.description,
highlights: [],
};

// ensure the end date is after the start date directly
if (position.endDate) {
object.endDate = `${position.endDate}`;
} else {
delete object.endDate;
}

return object;
Expand All @@ -136,15 +162,20 @@ class LinkedInToJsonResume {
function processEducation(education) {
let object = <Education>{
institution: education.schoolName,
url: "",
area: "",
studyType: education.degree,
startDate: `${education.startDate}`,
gpa: "",
endDate: "",
score: "",
courses: [],
};

// ensure the end date is after the start date directly
if (education.endDate) {
object.endDate = `${education.endDate}`;
} else {
delete object.endDate;
}

return object;
Expand Down Expand Up @@ -192,9 +223,11 @@ class LinkedInToJsonResume {
processProjects(projects) {
this.target.projects = projects.map((project) => ({
...{
name: project.title,
name: project.name,
startDate: `${project.startDate}`,
summary: project.description,
endDate: project.endDate ? `${project.endDate}` : "",
description: project.description,
highlights: project.highlights,
url: project.url,
},
...(project.endDate ? { endDate: `${project.endDate}` } : {}),
Expand All @@ -206,7 +239,7 @@ class LinkedInToJsonResume {
name: publication.name,
publisher: publication.publisher,
releaseDate: publication.date,
website: publication.url,
url: publication.url,
summary: publication.description,
}));
}
Expand All @@ -216,6 +249,69 @@ class LinkedInToJsonResume {
phone: number.number,
});
}

processAwards(awards) {
this.target.awards = awards.map((award) => ({
...{
title: award.title,
date: `${award.date}`,
awarder: award.awarder,
summary: award.summary,
},
}));
}

processCertificates(certificates) {
this.target.certificates = certificates.map((certificate) => ({
...{
name: certificate.name,
date: `${certificate.date}`,
issuer: certificate.issuer,
url: certificate.url,
},
}));
}

processEndorsements(endorsements) {
// This will update the level in this.target.skills
// according to how many endorsements the skill has
const processedSkills = new Set<string>();
this.target.skills.map((skill) => {
endorsements.filter((endorsement) => {
if (endorsement.name === skill.name) {
skill.level = endorsement.level;
processedSkills.add(skill.name);
}
});
});

// This will add the skills that have endorsements but
// are not in the skills array
endorsements.forEach((endorsement) => {
if (!processedSkills.has(endorsement.name)) {
this.target.skills.push({ ...endorsement, keywords: [] });
processedSkills.add(endorsement.name);
}
});

// Sort the skills by level
this.target.skills.sort((a, b) => {
return b.level - a.level;
});
}

processFollows(interests) {
if (!this.target.interests) {
this.target.interests = [];
}
interests.forEach((interest) => {
if (
this.target.interests &&
this.target.interests.filter((i) => i.name === interest).length === 0
)
this.target.interests.push({... interest});
});
}
}

export default LinkedInToJsonResume;
98 changes: 93 additions & 5 deletions src/js/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ function fileSelectHandler(e) {
};

getEntries(file, (entries) => {
// Ensuring that "Profile.csv" is the first file in the zip
// Solving the phone number being processed before the profile
// Find the index of "Profile.csv", move it to the beginning
const profileIndex = entries.findIndex(
(entry) => entry.filename === "Profile.csv"
);
if (profileIndex !== -1) {
const [profileEntry] = entries.splice(profileIndex, 1);
entries.unshift(profileEntry);
}

const promises = entries.map((entry) => {
switch (true) {
case entry.filename.indexOf("Skills.csv") !== -1:
Expand Down Expand Up @@ -160,7 +171,7 @@ function fileSelectHandler(e) {
return;
});

case entry.filename.indexOf("Recommendations Received.csv") !== -1:
case entry.filename.indexOf("Recommendations_Received.csv") !== -1:
return readEntryContents(entry).then((contents) => {
const elements = csvToArray(contents);
const recommendations = elements
Expand Down Expand Up @@ -207,7 +218,9 @@ function fileSelectHandler(e) {
return readEntryContents(entry).then((contents) => {
const elements = csvToArray(contents, "\t"); // yes, recommendations use tab-delimiter
const email = elements
.slice(1, elements.length - 1)
.flat()
.slice(1)
.map(elem => (elem as string).split(","))
.map((elem) => ({
address: elem[0],
status: elem[1],
Expand Down Expand Up @@ -239,13 +252,14 @@ function fileSelectHandler(e) {
const projects = elements
.slice(1, elements.length - 1)
.map((elem) => ({
title: elem[0],
description: elem[1],
url: elem[2],
name: elem[0],
startDate: moment(elem[3]).format("YYYY-MM-DD"),
endDate: elem[4]
? moment(elem[4]).format("YYYY-MM-DD")
: null,
description: elem[1],
highlights: [],
url: elem[2],
}));
linkedinToJsonResume.processProjects(
projects.sort(
Expand Down Expand Up @@ -275,6 +289,7 @@ function fileSelectHandler(e) {

case entry.filename.indexOf("PhoneNumbers.csv") !== -1:
return readEntryContents(entry).then((contents) => {

const elements = csvToArray(contents);
elements.shift();
const elementsWithNumber = elements.filter(
Expand All @@ -291,6 +306,79 @@ function fileSelectHandler(e) {
return;
});

case entry.filename.indexOf("Honors.csv") !== -1:
return readEntryContents(entry).then((contents) => {
const elements = csvToArray(contents);
const awards = elements
.slice(1, elements.length - 1)
.map((elem) => ({
title: elem[0],
date: moment(elem[3]).format("YYYY-MM-DD"),
awarder: "",
summary: elem[1],
}));
linkedinToJsonResume.processAwards(
awards.sort((p1, p2) => -p1.date.localeCompare(p2.date))
);
return;
});

case entry.filename.indexOf("Certifications.csv") !== -1:
return readEntryContents(entry).then((contents) => {
const elements = csvToArray(contents);
const certificates = elements
.slice(1, elements.length - 1)
.map((elem) => ({
name: elem[0],
date: moment(elem[3]).format("YYYY-MM-DD"),
issuer: elem[2],
summary: "",
}));
linkedinToJsonResume.processCertificates(
certificates.sort((p1, p2) => -p1.date.localeCompare(p2.date))
);
return;
});

case entry.filename.indexOf("Endorsement_Received_Info.csv") !== -1:
return readEntryContents(entry).then((contents) => {
const elements = csvToArray(contents);

// increment the endorsement count every time the name is found
const endorsements: {name: string; level: number}[] = [];
(elements as [])
.slice(1, elements.length - 1)
.forEach((elem) => {
const current = endorsements.find((endorsement) => endorsement.name === elem[1]);
if (current) {
current.level += 1;
} else {
endorsements.push({
name: elem[1],
level: 1,
});
}
}
);
linkedinToJsonResume.processEndorsements(endorsements);
return;
});

case entry.filename.indexOf("Causes You Care About.csv") !== -1:
case entry.filename.indexOf("Company Follows.csv") !== -1:
case entry.filename.indexOf("Hashtag_Follows.csv") !== -1:
return readEntryContents(entry).then((contents) => {
const elements = csvToArray(contents);
const interests = elements
.slice(1, elements.length - 1)
.map((elem) => ({
name: elem[0],
keywords: [],
}));
linkedinToJsonResume.processFollows(interests);
return;
});

default:
return Promise.resolve([]);
}
Expand Down
Loading