@financial-times/community-event-client
Advanced tools
Comparing version 1.1.2 to 1.2.0-alpha.0
@@ -6,2 +6,8 @@ # Change Log | ||
# [1.2.0-alpha.0](https://github.com/Financial-Times/community-promo-kit/compare/@financial-times/community-event-client@1.1.2...@financial-times/community-event-client@1.2.0-alpha.0) (2023-09-21) | ||
### Features | ||
- **@financial-times/community-event-client:** added support for reco… ([#85](https://github.com/Financial-Times/community-promo-kit/issues/85)) ([faea33b](https://github.com/Financial-Times/community-promo-kit/commit/faea33b26292db6bb8a6ea015b506d9097fcd727)) | ||
## [1.1.2](https://github.com/Financial-Times/community-promo-kit/compare/@financial-times/community-event-client@1.1.2-alpha.2074.1...@financial-times/community-event-client@1.1.2) (2023-09-19) | ||
@@ -8,0 +14,0 @@ |
import { Annotation, LiveEvent } from "./live-events-response.model"; | ||
export declare function getRecommendedEventsForContent(contentAnnotations: Annotation[][], events: LiveEvent[], numEvents: number): LiveEvent[]; | ||
export declare function getRecommendedEventsForContent(searchQuery: string, contentAnnotations: Annotation[][], events: LiveEvent[], numEvents: number): LiveEvent[]; | ||
//# sourceMappingURL=get-recommended-events-for-content.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getRecommendedEventsForContent = void 0; | ||
function tokenize(text) { | ||
return text.toLowerCase().match(/\b\w+\b/g) || []; | ||
} | ||
function keywordMatchScore(eventTitleKeywords, searchKeywords) { | ||
let score = 0; | ||
searchKeywords.forEach((keyword) => { | ||
if (eventTitleKeywords.includes(keyword)) { | ||
score += 1; // Increment by 1 for each matched keyword | ||
} | ||
}); | ||
return score; | ||
} | ||
function getWeightings(contentAnnotations) { | ||
@@ -49,3 +61,7 @@ const aboutWeighting = {}; | ||
} | ||
function getRecommendedEventsForContent(contentAnnotations, events, numEvents) { | ||
function sumCosineSimilarityAndKeywordsScores(cosineSimilarityScore, keywordsScore) { | ||
const [cosineSimilarityWeight, keywordsScoreWeight] = [0.3, 0.7]; | ||
return cosineSimilarityScore * cosineSimilarityWeight + keywordsScore * keywordsScoreWeight; | ||
} | ||
function getRecommendedEventsForContent(searchQuery, contentAnnotations, events, numEvents) { | ||
if (!Array.isArray(contentAnnotations) || | ||
@@ -61,3 +77,6 @@ !Array.isArray(events) || | ||
const mapEventsScores = new Map(); | ||
const searchKeywords = tokenize(searchQuery); | ||
events.forEach((event) => { | ||
const eventTitleKeywods = tokenize(event.title); | ||
const keywordScore = keywordMatchScore(eventTitleKeywods, searchKeywords); | ||
const eventWeightings = getWeightings([event.annotations || []]); | ||
@@ -72,4 +91,4 @@ const eventAboutMagnitute = calculateMagnitude(eventWeightings.aboutWeighting); | ||
mapEventsScores.set(event.uuid, { | ||
normalisedAboutScore, | ||
normalisedImplicitlyAboutScore, | ||
sumAboutAndKeywordsScores: sumCosineSimilarityAndKeywordsScores(normalisedAboutScore, keywordScore), | ||
sumImplicitlyAboutAndKeywordsScore: sumCosineSimilarityAndKeywordsScores(normalisedImplicitlyAboutScore, keywordScore), | ||
}); | ||
@@ -79,17 +98,16 @@ }); | ||
const scoreEvent1 = mapEventsScores.get(event1.uuid) || { | ||
normalisedAboutScore: 0, | ||
normalisedImplicitlyAboutScore: 0, | ||
sumAboutAndKeywordsScores: 0, | ||
sumImplicitlyAboutAndKeywordsScore: 0, | ||
}; | ||
const scoreEvent2 = mapEventsScores.get(event2.uuid) || { | ||
normalisedAboutScore: 0, | ||
normalisedImplicitlyAboutScore: 0, | ||
sumAboutAndKeywordsScores: 0, | ||
sumImplicitlyAboutAndKeywordsScore: 0, | ||
}; | ||
if (scoreEvent1?.normalisedAboutScore !== scoreEvent2?.normalisedAboutScore) { | ||
return ((scoreEvent2.normalisedAboutScore || 0) - | ||
(scoreEvent1.normalisedAboutScore || 0)); | ||
if (scoreEvent1?.sumAboutAndKeywordsScores !== scoreEvent2?.sumAboutAndKeywordsScores) { | ||
return ((scoreEvent2.sumAboutAndKeywordsScores || 0) - (scoreEvent1.sumAboutAndKeywordsScores || 0)); | ||
} | ||
else if (scoreEvent1?.normalisedImplicitlyAboutScore !== | ||
scoreEvent2?.normalisedImplicitlyAboutScore) { | ||
return (scoreEvent2.normalisedImplicitlyAboutScore - | ||
scoreEvent1.normalisedImplicitlyAboutScore); | ||
else if (scoreEvent1?.sumImplicitlyAboutAndKeywordsScore !== | ||
scoreEvent2?.sumImplicitlyAboutAndKeywordsScore) { | ||
return (scoreEvent2.sumImplicitlyAboutAndKeywordsScore - | ||
scoreEvent1.sumImplicitlyAboutAndKeywordsScore); | ||
} | ||
@@ -96,0 +114,0 @@ else { |
{ | ||
"name": "@financial-times/community-event-client", | ||
"version": "1.1.2", | ||
"version": "1.2.0-alpha.0", | ||
"description": "A client for retrieving community-events", | ||
@@ -27,3 +27,3 @@ "main": "dist/serverside.js", | ||
}, | ||
"gitHead": "8b5464362229b9e2ab6054c8d44848bb162b6728" | ||
"gitHead": "67a305da8d5b73c4696277b8ff3ebf7681ae5f5d" | ||
} |
import { Annotation, LiveEvent } from "./live-events-response.model"; | ||
function tokenize(text) { | ||
return text.toLowerCase().match(/\b\w+\b/g) || []; | ||
} | ||
function keywordMatchScore(eventTitleKeywords, searchKeywords) { | ||
let score = 0; | ||
searchKeywords.forEach((keyword) => { | ||
if (eventTitleKeywords.includes(keyword)) { | ||
score += 1; // Increment by 1 for each matched keyword | ||
} | ||
}); | ||
return score; | ||
} | ||
function getWeightings(contentAnnotations: Annotation[][]): { | ||
@@ -14,4 +30,3 @@ aboutWeighting: { [key: string]: number }; | ||
const isAboutPredicate = annotation?.predicate?.includes("about"); | ||
const isImplicitlyAbout = | ||
annotation?.predicate?.includes("implicitlyAbout"); | ||
const isImplicitlyAbout = annotation?.predicate?.includes("implicitlyAbout"); | ||
@@ -66,3 +81,10 @@ if (isAboutPredicate) { | ||
function sumCosineSimilarityAndKeywordsScores(cosineSimilarityScore, keywordsScore) { | ||
const [cosineSimilarityWeight, keywordsScoreWeight] = [0.3, 0.7]; | ||
return cosineSimilarityScore * cosineSimilarityWeight + keywordsScore * keywordsScoreWeight; | ||
} | ||
export function getRecommendedEventsForContent( | ||
searchQuery: string, | ||
contentAnnotations: Annotation[][], | ||
@@ -82,5 +104,3 @@ events: LiveEvent[], | ||
const contentWeightings = getWeightings(contentAnnotations); | ||
const contentMagnituteAbout = calculateMagnitude( | ||
contentWeightings.aboutWeighting | ||
); | ||
const contentMagnituteAbout = calculateMagnitude(contentWeightings.aboutWeighting); | ||
const contentMagnituteImplicitlyAbout = calculateMagnitude( | ||
@@ -91,10 +111,11 @@ contentWeightings.implicitlyAboutWeighting | ||
string, | ||
{ normalisedAboutScore: number; normalisedImplicitlyAboutScore: number } | ||
{ sumAboutAndKeywordsScores: number; sumImplicitlyAboutAndKeywordsScore: number } | ||
>(); | ||
const searchKeywords = tokenize(searchQuery); | ||
events.forEach((event) => { | ||
const eventTitleKeywods = tokenize(event.title); | ||
const keywordScore = keywordMatchScore(eventTitleKeywods, searchKeywords); | ||
const eventWeightings = getWeightings([event.annotations || []]); | ||
const eventAboutMagnitute = calculateMagnitude( | ||
eventWeightings.aboutWeighting | ||
); | ||
const eventAboutMagnitute = calculateMagnitude(eventWeightings.aboutWeighting); | ||
const eventImplicitlyAboutMagnitute = calculateMagnitude( | ||
@@ -111,5 +132,3 @@ eventWeightings.implicitlyAboutWeighting | ||
); | ||
const normalisedAboutScore = | ||
eventRawAboutScore / (contentMagnituteAbout * eventAboutMagnitute); | ||
const normalisedAboutScore = eventRawAboutScore / (contentMagnituteAbout * eventAboutMagnitute); | ||
const normalisedImplicitlyAboutScore = | ||
@@ -120,4 +139,10 @@ eventRawImplicitlyAboutScore / | ||
mapEventsScores.set(event.uuid, { | ||
normalisedAboutScore, | ||
normalisedImplicitlyAboutScore, | ||
sumAboutAndKeywordsScores: sumCosineSimilarityAndKeywordsScores( | ||
normalisedAboutScore, | ||
keywordScore | ||
), | ||
sumImplicitlyAboutAndKeywordsScore: sumCosineSimilarityAndKeywordsScores( | ||
normalisedImplicitlyAboutScore, | ||
keywordScore | ||
), | ||
}); | ||
@@ -128,24 +153,21 @@ }); | ||
const scoreEvent1 = mapEventsScores.get(event1.uuid) || { | ||
normalisedAboutScore: 0, | ||
normalisedImplicitlyAboutScore: 0, | ||
sumAboutAndKeywordsScores: 0, | ||
sumImplicitlyAboutAndKeywordsScore: 0, | ||
}; | ||
const scoreEvent2 = mapEventsScores.get(event2.uuid) || { | ||
normalisedAboutScore: 0, | ||
normalisedImplicitlyAboutScore: 0, | ||
sumAboutAndKeywordsScores: 0, | ||
sumImplicitlyAboutAndKeywordsScore: 0, | ||
}; | ||
if ( | ||
scoreEvent1?.normalisedAboutScore !== scoreEvent2?.normalisedAboutScore | ||
) { | ||
if (scoreEvent1?.sumAboutAndKeywordsScores !== scoreEvent2?.sumAboutAndKeywordsScores) { | ||
return ( | ||
(scoreEvent2.normalisedAboutScore || 0) - | ||
(scoreEvent1.normalisedAboutScore || 0) | ||
(scoreEvent2.sumAboutAndKeywordsScores || 0) - (scoreEvent1.sumAboutAndKeywordsScores || 0) | ||
); | ||
} else if ( | ||
scoreEvent1?.normalisedImplicitlyAboutScore !== | ||
scoreEvent2?.normalisedImplicitlyAboutScore | ||
scoreEvent1?.sumImplicitlyAboutAndKeywordsScore !== | ||
scoreEvent2?.sumImplicitlyAboutAndKeywordsScore | ||
) { | ||
return ( | ||
scoreEvent2.normalisedImplicitlyAboutScore - | ||
scoreEvent1.normalisedImplicitlyAboutScore | ||
scoreEvent2.sumImplicitlyAboutAndKeywordsScore - | ||
scoreEvent1.sumImplicitlyAboutAndKeywordsScore | ||
); | ||
@@ -152,0 +174,0 @@ } else { |
@@ -7,12 +7,19 @@ import { getRecommendedEventsForContent } from "../../src/live-events-poller/get-recommended-events-for-content"; | ||
it("should return recommended events based on content annotations", () => { | ||
const recommendedEvents = getRecommendedEventsForContent("", contentAnnotations, liveEvents, 2); | ||
expect(recommendedEvents).toHaveLength(2); | ||
expect(recommendedEvents[0].uuid).toBe("event1"); | ||
expect(recommendedEvents[1].uuid).toBe("event3"); | ||
}); | ||
it("should return recommended events based on search query", () => { | ||
const recommendedEvents = getRecommendedEventsForContent( | ||
"event 3", | ||
contentAnnotations, | ||
liveEvents, | ||
2 | ||
1 | ||
); | ||
console.log(`RecommendedEvents ${JSON.stringify(recommendedEvents)}`); | ||
expect(recommendedEvents).toHaveLength(2); | ||
expect(recommendedEvents[0].uuid).toBe("event1"); | ||
expect(recommendedEvents[1].uuid).toBe("event3"); | ||
expect(recommendedEvents).toHaveLength(1); | ||
expect(recommendedEvents[0].uuid).toBe("event3"); | ||
}); | ||
@@ -66,2 +73,3 @@ | ||
const recommendedEvents = getRecommendedEventsForContent( | ||
"", | ||
contentAnnotations, | ||
@@ -122,2 +130,3 @@ eventsWithTiebreaker, | ||
const recommendedEvents = getRecommendedEventsForContent( | ||
"", | ||
contentAnnotations, | ||
@@ -133,7 +142,3 @@ eventsWithTiebreaker, | ||
it("should return empty array if no events available", () => { | ||
const recommendedEvents = getRecommendedEventsForContent( | ||
contentAnnotations, | ||
[], | ||
2 | ||
); | ||
const recommendedEvents = getRecommendedEventsForContent("", contentAnnotations, [], 2); | ||
@@ -144,7 +149,3 @@ expect(recommendedEvents).toHaveLength(0); | ||
it("should return all events if numEvents is greater than available events", () => { | ||
const recommendedEvents = getRecommendedEventsForContent( | ||
contentAnnotations, | ||
liveEvents, | ||
5 | ||
); | ||
const recommendedEvents = getRecommendedEventsForContent("", contentAnnotations, liveEvents, 5); | ||
@@ -151,0 +152,0 @@ expect(recommendedEvents).toHaveLength(3); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
109114
1914
2