From 542842f1e3f6d4c22e2bc8cf7104993c95c1f0ac Mon Sep 17 00:00:00 2001 From: Julien DELCLOS Date: Mon, 11 Nov 2024 15:39:16 +0100 Subject: [PATCH 1/3] Fix color, personality, traits raw data extraction The banner were different for the personality and the traits, so the extraction was not working properly. Not sure about the color extraction, but added fallback colors for each types. --- .../community/16personalities/index.mjs | 132 ++++++++++++++---- .../classic/partials/16personalities.ejs | 29 +++- 2 files changed, 127 insertions(+), 34 deletions(-) diff --git a/source/plugins/community/16personalities/index.mjs b/source/plugins/community/16personalities/index.mjs index 92d8fb28a21..5a4ad4ae144 100644 --- a/source/plugins/community/16personalities/index.mjs +++ b/source/plugins/community/16personalities/index.mjs @@ -1,62 +1,134 @@ -//Setup +// Setup export default async function({login, q, imports, data, account}, {enabled = false, extras = false} = {}) { - //Plugin execution try { - //Check if plugin is enabled and requirements are met + // Check if the plugin is enabled and requirements are met if ((!q["16personalities"]) || (!imports.metadata.plugins["16personalities"].enabled(enabled, {extras}))) return null - //Load inputs + // Load inputs let {url, sections, scores} = imports.metadata.plugins["16personalities"].inputs({data, account, q}) if (!url) throw {error: {message: "URL is not set"}} - //Start puppeteer and navigate to page + // Start puppeteer and navigate to page console.debug(`metrics/compute/${login}/plugins > 16personalities > starting browser`) const browser = await imports.puppeteer.launch() console.debug(`metrics/compute/${login}/plugins > 16personalities > started ${await browser.version()}`) const page = await browser.newPage() console.debug(`metrics/compute/${login}/plugins > 16personalities > loading ${url}`) - await page.goto(url, {waitUntil: imports.puppeteer.events}) - - //Fetch raw data - const raw = await page.evaluate(() => ({ - color: getComputedStyle(document.querySelector(".card__bg")).backgroundColor, //eslint-disable-line no-undef - type: document.querySelector(".type__code").innerText, - personality: [...document.querySelectorAll(".personality-cards .sp-personality-card")].map(card => ({ - category: card.querySelector(".card__title").innerText, - value: card.querySelector(".card__subtitle").innerText, - image: card.querySelector(".card__image").src, - text: card.querySelector(".card__text").innerText, - })), - traits: [...document.querySelectorAll("#traits .card__body")].map(card => ({ - category: card.querySelector(".card__title").innerText, - value: card.querySelector(".card__subtitle").innerText, - score: card.querySelector(".center__num").innerText, - text: card.querySelector("p").innerText, - })), - })) - //Format data + // Capture console messages from the browser context + page.on('console', msg => { + if (msg.type() === 'debug') { + console.debug(`BROWSER: ${msg.text()}`); + } + }) + + await page.goto(url, {waitUntil: 'networkidle2'}) + + // Fetch raw data + const raw = await page.evaluate(() => { + const getInnerText = (selector) => document.querySelector(selector)?.innerText || "" + + // Default map personality category to RGB colors + const defaultPersonalityColors = { + explorers: 'rgb(228, 174, 58)', // Virtuoso, Adventurer, Entrepreneur, Entertainer + sentinels: 'rgb(66, 152, 180)', // Logistician, Defender, Executive, Consul + diplomats: 'rgb(51, 164, 116)', // Advocate, Mediator, Protagonist, Campaigner + analysts: 'rgb(136, 97, 154)', // Architect, Logician, Commander, Debater + default: 'rgb(0, 0, 0)' + } + let defaultColor = defaultPersonalityColors.default + + // Choose the default color based on the personality type + const personalityType = getInnerText(".link--inline"); + if (personalityType.includes("Virtuoso") || personalityType.includes("Adventurer") || personalityType.includes("Entrepreneur") || personalityType.includes("Entertainer")) + defaultColor = defaultPersonalityColors.explorers + else if (personalityType.includes("Logistician") || personalityType.includes("Defender") || personalityType.includes("Executive") || personalityType.includes("Consul")) + defaultColor = defaultPersonalityColors.sentinels + else if (personalityType.includes("Advocate") || personalityType.includes("Mediator") || personalityType.includes("Protagonist") || personalityType.includes("Campaigner")) + defaultColor = defaultPersonalityColors.diplomats + else if (personalityType.includes("Architect") || personalityType.includes("Logician") || personalityType.includes("Commander") || personalityType.includes("Debater")) + defaultColor = defaultPersonalityColors.analysts; + + console.debug(`Personality Type: ${personalityType}`) + + return { + // Type extraction + type: getInnerText(".type__code"), + + // Personality details extraction + personality: [...document.querySelectorAll(".slider__slides > div")].map(card => { + // Extract image data + let image = "" + const cardElement = card.querySelector(".card__image") + // Check if the card has an image as an url, e.g., the "His Role" image or the "His Strategy" image + if (cardElement.querySelector("img")) { + image = cardElement.querySelector("img").src + console.debug(`Image for ${card.querySelector(".card__title")?.innerText}: ${image}`) + } + // Check if the card has a image as a svg, e.g., the "His personality" image + else if (cardElement.querySelector("svg")) { + image = new XMLSerializer().serializeToString(cardElement.querySelector("svg")) + image = `data:image/svg+xml,${encodeURIComponent(image)}` + console.debug(`Image for ${card.querySelector(".card__title")?.innerText} is a svg`) + } + + return { + category: card.querySelector(".card__title")?.innerText || "", // Category, e.g., "His role" + value: card.querySelector(".card__subtitle")?.innerText || "", // Value of the category, e.g., "Sentinel" + image, // Image of the category + text: card.querySelector(".prevent--drag.card__p")?.innerText || "" // Description of the category + } + }), + + // Traits details extraction + traits: [...document.querySelectorAll(".traits__boxes > div")].map(card => { + const categoryText = card.querySelector(".traitbox__label")?.innerText + const scoreText = card.querySelector(".traitbox__value")?.innerText.trim() // Get the text like "75% Extraverted" + + console.debug(`Parsing Trait category ${categoryText} ${scoreText}`); + + // Split the score text into percentage and trait + const [percentage, ...traitArray] = scoreText.split(' '); + + // Return the traits details + return { + category: categoryText || "", // Trait category name, e.g., "Energy" + value: traitArray.join(" ") || "", // Extracted trait, e.g., "Extraverted" + score: percentage || "", // Extracted percentage, e.g., "75%" + text: card.querySelector("p").innerText || "" // Description of the trait + } + }), + + // Color + color: document.querySelector(".card__bg") ? getComputedStyle(document.querySelector(".card__bg")).backgroundColor : defaultColor + } + }) + + // Format data const {color} = raw const type = raw.type.replace("(", "").replace(")", "").trim() const personality = await Promise.all(raw.personality.map(async ({category, value, image, text}) => ({ category, value: value.replace(`(${type})`, "").trim(), - image: await imports.imgb64(image), + image: image.startsWith("data:image/svg+xml,") ? image : await imports.imgb64(image), text: text.replace(`${category}\n${value}\n`, "").trim(), }))) const traits = raw.traits.map(({category, value, score, text}) => ({ - category, + category: category.replace(":", "").trim(), value: `${value[0]}${value.substring(1).toLocaleLowerCase()}`, score: scores ? Number(score.replace("%", "")) / 100 : NaN, - text: text.split(".").slice(1).join("."), + text: text.trim() })) - //Results + // Close browser + await browser.close(); + + // Results return {sections, color, type, personality, traits} } - //Handle errors + // Handle errors catch (error) { throw imports.format.error(error) } diff --git a/source/templates/classic/partials/16personalities.ejs b/source/templates/classic/partials/16personalities.ejs index 48883f7959e..c8a8ba94684 100644 --- a/source/templates/classic/partials/16personalities.ejs +++ b/source/templates/classic/partials/16personalities.ejs @@ -14,14 +14,34 @@ <% } else { %> - <% if ((plugins["16personalities"].sections.includes("personality"))||(plugins["16personalities"].sections.includes("profile"))) { %> + + <% if (plugins["16personalities"].sections.includes("personality") || plugins["16personalities"].sections.includes("profile")) { %>
- <% for (const {category, value, image, text} of plugins["16personalities"].personality) { if (((!plugins["16personalities"].sections.includes("personality"))&&(/personality/i.test(category)))||((!plugins["16personalities"].sections.includes("profile"))&&(!/personality/i.test(category)))) continue %> + <% for (const {category, value, image, text} of plugins["16personalities"].personality) { %> + <% + // Conditional check to filter categories if needed + if ( + (!plugins["16personalities"].sections.includes("personality") && /personality/i.test(category)) || + (!plugins["16personalities"].sections.includes("profile") && !/personality/i.test(category)) + ) continue; + %>
- + + <% if (image.startsWith(' + +
+ <%= category %> +
+ <% } else { %> + + <%= category %> + <% } %> +
- ;"><%= value %> <%= category.toLocaleLowerCase() %> + ;"> + <%= value %> <%= category.toLowerCase() %> + <%= text %>
@@ -29,6 +49,7 @@
<% } %> + <% if (plugins["16personalities"].sections.includes("traits")) { %>
From a9ccffcf1bf9f850806bdec78e890860000d08e5 Mon Sep 17 00:00:00 2001 From: Julien DELCLOS Date: Mon, 11 Nov 2024 16:54:11 +0100 Subject: [PATCH 2/3] eslint --- .../community/16personalities/index.mjs | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/source/plugins/community/16personalities/index.mjs b/source/plugins/community/16personalities/index.mjs index 5a4ad4ae144..2cd5bd0182c 100644 --- a/source/plugins/community/16personalities/index.mjs +++ b/source/plugins/community/16personalities/index.mjs @@ -1,47 +1,47 @@ -// Setup +//Setup export default async function({login, q, imports, data, account}, {enabled = false, extras = false} = {}) { try { - // Check if the plugin is enabled and requirements are met + //Check if the plugin is enabled and requirements are met if ((!q["16personalities"]) || (!imports.metadata.plugins["16personalities"].enabled(enabled, {extras}))) return null - // Load inputs + //Load inputs let {url, sections, scores} = imports.metadata.plugins["16personalities"].inputs({data, account, q}) if (!url) throw {error: {message: "URL is not set"}} - // Start puppeteer and navigate to page + //Start puppeteer and navigate to page console.debug(`metrics/compute/${login}/plugins > 16personalities > starting browser`) const browser = await imports.puppeteer.launch() console.debug(`metrics/compute/${login}/plugins > 16personalities > started ${await browser.version()}`) const page = await browser.newPage() console.debug(`metrics/compute/${login}/plugins > 16personalities > loading ${url}`) - // Capture console messages from the browser context - page.on('console', msg => { - if (msg.type() === 'debug') { - console.debug(`BROWSER: ${msg.text()}`); + //Capture console messages from the browser context + page.on("console", msg => { + if (msg.type() === "debug") { + console.debug(`BROWSER: ${msg.text()}`) } }) - await page.goto(url, {waitUntil: 'networkidle2'}) + await page.goto(url, {waitUntil: "networkidle2"}) - // Fetch raw data + //Fetch raw data const raw = await page.evaluate(() => { - const getInnerText = (selector) => document.querySelector(selector)?.innerText || "" + const getInnerText = selector => document.querySelector(selector)?.innerText || "" - // Default map personality category to RGB colors + //Default map personality category to RGB colors const defaultPersonalityColors = { - explorers: 'rgb(228, 174, 58)', // Virtuoso, Adventurer, Entrepreneur, Entertainer - sentinels: 'rgb(66, 152, 180)', // Logistician, Defender, Executive, Consul - diplomats: 'rgb(51, 164, 116)', // Advocate, Mediator, Protagonist, Campaigner - analysts: 'rgb(136, 97, 154)', // Architect, Logician, Commander, Debater - default: 'rgb(0, 0, 0)' + explorers: "rgb(228, 174, 58)", //Virtuoso, Adventurer, Entrepreneur, Entertainer + sentinels: "rgb(66, 152, 180)", //Logistician, Defender, Executive, Consul + diplomats: "rgb(51, 164, 116)", //Advocate, Mediator, Protagonist, Campaigner + analysts: "rgb(136, 97, 154)", //Architect, Logician, Commander, Debater + default: "rgb(0, 0, 0)" } let defaultColor = defaultPersonalityColors.default - // Choose the default color based on the personality type - const personalityType = getInnerText(".link--inline"); + //Choose the default color based on the personality type + const personalityType = getInnerText(".link--inline") if (personalityType.includes("Virtuoso") || personalityType.includes("Adventurer") || personalityType.includes("Entrepreneur") || personalityType.includes("Entertainer")) defaultColor = defaultPersonalityColors.explorers else if (personalityType.includes("Logistician") || personalityType.includes("Defender") || personalityType.includes("Executive") || personalityType.includes("Consul")) @@ -49,25 +49,25 @@ export default async function({login, q, imports, data, account}, {enabled = fal else if (personalityType.includes("Advocate") || personalityType.includes("Mediator") || personalityType.includes("Protagonist") || personalityType.includes("Campaigner")) defaultColor = defaultPersonalityColors.diplomats else if (personalityType.includes("Architect") || personalityType.includes("Logician") || personalityType.includes("Commander") || personalityType.includes("Debater")) - defaultColor = defaultPersonalityColors.analysts; + defaultColor = defaultPersonalityColors.analysts console.debug(`Personality Type: ${personalityType}`) return { - // Type extraction + //Type extraction type: getInnerText(".type__code"), - // Personality details extraction + //Personality details extraction personality: [...document.querySelectorAll(".slider__slides > div")].map(card => { - // Extract image data + //Extract image data let image = "" const cardElement = card.querySelector(".card__image") - // Check if the card has an image as an url, e.g., the "His Role" image or the "His Strategy" image + //Check if the card has an image as an url, e.g., the "His Role" image or the "His Strategy" image if (cardElement.querySelector("img")) { image = cardElement.querySelector("img").src console.debug(`Image for ${card.querySelector(".card__title")?.innerText}: ${image}`) } - // Check if the card has a image as a svg, e.g., the "His personality" image + //Check if the card has a image as a svg, e.g., the "His personality" image else if (cardElement.querySelector("svg")) { image = new XMLSerializer().serializeToString(cardElement.querySelector("svg")) image = `data:image/svg+xml,${encodeURIComponent(image)}` @@ -75,38 +75,38 @@ export default async function({login, q, imports, data, account}, {enabled = fal } return { - category: card.querySelector(".card__title")?.innerText || "", // Category, e.g., "His role" - value: card.querySelector(".card__subtitle")?.innerText || "", // Value of the category, e.g., "Sentinel" - image, // Image of the category - text: card.querySelector(".prevent--drag.card__p")?.innerText || "" // Description of the category + category: card.querySelector(".card__title")?.innerText || "", //Category, e.g., "His role" + value: card.querySelector(".card__subtitle")?.innerText || "", //Value of the category, e.g., "Sentinel" + image, //Image of the category + text: card.querySelector(".prevent--drag.card__p")?.innerText || "" //Description of the category } }), - // Traits details extraction + //Traits details extraction traits: [...document.querySelectorAll(".traits__boxes > div")].map(card => { const categoryText = card.querySelector(".traitbox__label")?.innerText - const scoreText = card.querySelector(".traitbox__value")?.innerText.trim() // Get the text like "75% Extraverted" + const scoreText = card.querySelector(".traitbox__value")?.innerText.trim() //Get the text like "75% Extraverted" - console.debug(`Parsing Trait category ${categoryText} ${scoreText}`); + console.debug(`Parsing Trait category ${categoryText} ${scoreText}`) - // Split the score text into percentage and trait - const [percentage, ...traitArray] = scoreText.split(' '); + //Split the score text into percentage and trait + const [percentage, ...traitArray] = scoreText.split(" ") - // Return the traits details + //Return the traits details return { - category: categoryText || "", // Trait category name, e.g., "Energy" - value: traitArray.join(" ") || "", // Extracted trait, e.g., "Extraverted" - score: percentage || "", // Extracted percentage, e.g., "75%" - text: card.querySelector("p").innerText || "" // Description of the trait + category: categoryText || "", //Trait category name, e.g., "Energy" + value: traitArray.join(" ") || "", //Extracted trait, e.g., "Extraverted" + score: percentage || "", //Extracted percentage, e.g., "75%" + text: card.querySelector("p").innerText || "" //Description of the trait } }), - // Color - color: document.querySelector(".card__bg") ? getComputedStyle(document.querySelector(".card__bg")).backgroundColor : defaultColor + //Color + color: document.querySelector(".card__bg") ? getComputedStyle(document.querySelector(".card__bg")).backgroundColor : defaultColor //eslint-disable-line no-undef } }) - // Format data + //Format data const {color} = raw const type = raw.type.replace("(", "").replace(")", "").trim() const personality = await Promise.all(raw.personality.map(async ({category, value, image, text}) => ({ @@ -122,13 +122,13 @@ export default async function({login, q, imports, data, account}, {enabled = fal text: text.trim() })) - // Close browser - await browser.close(); + //Close browser + await browser.close() - // Results + //Results return {sections, color, type, personality, traits} } - // Handle errors + //Handle errors catch (error) { throw imports.format.error(error) } From f201c90cce39f45c0af60b0944d695208aad8db5 Mon Sep 17 00:00:00 2001 From: Julien DELCLOS Date: Mon, 11 Nov 2024 17:51:06 +0100 Subject: [PATCH 3/3] Empty commit to rettrigger the tests