Skip to content

Commit

Permalink
feat(hot-tracks): try list of recent posts sorted by number of reposts (
Browse files Browse the repository at this point in the history
#720)

Contributes to #718.

## What does this PR do / solve?

The current ranking algorithm of hot tracks is complicated and possibly buggy.

## Overview of changes

Just show the latest 20 posts that have been reposted at least once.
  • Loading branch information
adrienjoly committed Sep 7, 2023
1 parent 82206c5 commit 7b870c3
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 269 deletions.
27 changes: 2 additions & 25 deletions app/features/hot-tracks.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// @ts-check

const snip = require('../../app/snip.js');

const FIELDS_TO_SUM = {
nbP: true, // number of plays
nbL: true, // number of likes (from lov[] field)
Expand All @@ -18,30 +16,9 @@ const FIELDS_TO_COPY = {

exports.FIELDS_TO_COPY = FIELDS_TO_COPY;

const fieldList = Object.keys(FIELDS_TO_COPY)
.concat(Object.keys(FIELDS_TO_SUM))
.concat(['prev']);

function mergePostData(track, post) {
for (const f in fieldList) post[fieldList[f]] = track[fieldList[f]];
post.trackId = track._id;
post.rankIncr = track.prev - track.score;
return post;
}

/**
* @param {() => Promise<{pId: string, eId: string}[]>} getTracksByDescendingScore (partial type definition, just to check usage of objArrayToValueArray)
*/
exports.getHotTracks = async function (
getTracksByDescendingScore,
fetchPostsByPid,
) {
const tracks = await getTracksByDescendingScore();
const pidList = snip.objArrayToValueArray(tracks, 'pId');
const posts = await fetchPostsByPid(pidList);
// complete track items with additional metadata (from posts)
return tracks.map((track) => {
const post = posts.find(({ eId }) => eId === track.eId);
return post ? mergePostData(track, post) : track;
});
exports.getHotTracks = async function (getTracksByDescendingScore) {
return await getTracksByDescendingScore();
};
56 changes: 22 additions & 34 deletions app/models/track.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@

const config = require('./config.js');
const mongodb = require('./mongodb.js');
const ObjectId = mongodb.ObjectId;
const feature = require('../features/hot-tracks.js');

const { FIELDS_TO_SUM, FIELDS_TO_COPY } = feature;
Expand Down Expand Up @@ -86,25 +85,6 @@ exports.countTracksWithField = function (fieldName, cb) {
);
};

/* fetch top hot tracks, without processing */
exports.fetch = function (params, handler) {
params = params || {};
params.sort = params.sort || [['score', 'desc']];
mongodb.collections['track']
.find({ eId: { $ne: '/sc/undefined' } }, params) // exclude invalid eId values, cf https://github.com/openwhyd/openwhyd/issues/718#issuecomment-1710359006
.toArray()
.then(
function (results) {
// console.log('=> fetched ' + results.length + ' tracks');
if (handler) handler(results);
},
(err) => {
console.trace('trackModel.fetch', err);
handler();
},
);
};

exports.fetchTrackByEid = function (eId, cb) {
// in order to allow requests of soundcloud eId without hash (#):
const eidPrefix = ('' + eId).indexOf('/sc/') == 0 && ('' + eId).split('#')[0];
Expand All @@ -128,26 +108,34 @@ exports.fetchTrackByEid = function (eId, cb) {

// functions for fetching tracks and corresponding posts

const makeObjectIdList = (pId) =>
(pId && Array.isArray(pId) ? pId : []).map(function (id) {
return ObjectId('' + id);
});

function fetchPostsByPid(pId) {
return mongodb.collections['post']
.find({ _id: { $in: makeObjectIdList(pId) } }, POST_FETCH_OPTIONS)
.toArray();
async function getRecentPostsByDescendingNumberOfReposts(params) {
return (
await mongodb.collections['post']
.find(
{
eId: { $ne: '/sc/undefined' }, // exclude invalid eId values, cf https://github.com/openwhyd/openwhyd/issues/718#issuecomment-1710359006
nbR: { $gte: 1 },
},
params,
)
.sort({ _id: -1 })
.limit(20)
.toArray()
)
.sort((a, b) => b.nbR - a.nbR) // sort posts by number of times they were reposted by other users
.map((post) => ({
...post,
eId: post.eId,
pId: post._id.toString(),
score: post.nbR,
}));
}

/* fetch top hot tracks, and include complete post data (from the "post" collection), score, and rank increment */
exports.getHotTracksFromDb = function (params, handler) {
params.skip = parseInt(params.skip || 0);
const getTracksByDescendingScore = () =>
new Promise((resolve) => {
exports.fetch(params, resolve);
});
feature
.getHotTracks(getTracksByDescendingScore, fetchPostsByPid)
.getHotTracks(() => getRecentPostsByDescendingNumberOfReposts(params))
.then((tracks) =>
tracks.map((track) => ({
...track,
Expand Down
149 changes: 24 additions & 125 deletions test/approval/hot-tracks/__snapshots__/hot-tracks.approval.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Hot Tracks (approval tests - to be replaced later by unit tests) renders a limited number of ranked tracks 1`] = `
exports[`Hot Tracks (approval tests - to be replaced later by unit tests) renders a limited number of ranked posts 1`] = `
"{
hasMore: {
skip: 1
Expand All @@ -9,13 +9,15 @@ exports[`Hot Tracks (approval tests - to be replaced later by unit tests) render
{
_id: '61e19a3f078b4c9934e72ce5',
name: 'a popular track',
nbR: 2,
pId: '61e19a3f078b4c9934e72ce5',
score: 2
}
]
}"
`;

exports[`Hot Tracks (approval tests - to be replaced later by unit tests) renders a limited number of ranked tracks 2`] = `
exports[`Hot Tracks (approval tests - to be replaced later by unit tests) renders a limited number of ranked posts 2`] = `
"<!DOCTYPE html>
<html lang="en">
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# whydapp: http://ogp.me/ns/fb/whydapp#">
Expand Down Expand Up @@ -186,10 +188,10 @@ exports[`Hot Tracks (approval tests - to be replaced later by unit tests) render
<div class="stats">
<span
class="nbReposts"
style="display:none;"
style=""
onclick="javascript:showReposts('61e19a3f078b4c9934e72ce5')"
>
<span>0</span>
<span>2</span>
</span>
<span
class="nbLoves"
Expand Down Expand Up @@ -361,22 +363,22 @@ exports[`Hot Tracks (approval tests - to be replaced later by unit tests) render
{
_id: '61e19a3f078b4c9934e72ce5',
eId: '/yt/track_B',
rankIncr: null,
score: 1,
nbR: 2,
pId: '61e19a3f078b4c9934e72ce5',
score: 2,
text: 'my favorite track ever!',
trackId: '61e19a3f078b4c9934e72ce7',
trackUrl: '//youtube.com/watch?v=track_B'
},
{
_id: '61e19a3f078b4c9934e72ce4',
eId: '/yt/track_A',
nbR: 1,
pId: '61e19a3f078b4c9934e72ce4',
pl: {
id: 0,
name: 'soundtrack of my life'
},
rankIncr: null,
score: 0,
trackId: '61e19a3f078b4c9934e72ce6',
score: 1,
trackUrl: '//youtube.com/watch?v=track_A'
}
]
Expand Down Expand Up @@ -513,7 +515,7 @@ exports[`Hot Tracks (approval tests - to be replaced later by unit tests) render
<div
class="post "
data-pid="61e19a3f078b4c9934e72ce5"
data-score="1"
data-score="2"
data-order=""
data-initialpid=""
data-loved=""
Expand Down Expand Up @@ -554,10 +556,10 @@ exports[`Hot Tracks (approval tests - to be replaced later by unit tests) render
<div class="stats">
<span
class="nbReposts"
style="display:none;"
style=""
onclick="javascript:showReposts('61e19a3f078b4c9934e72ce5')"
>
<span>0</span>
<span>2</span>
</span>
<span
class="nbLoves"
Expand Down Expand Up @@ -622,7 +624,7 @@ exports[`Hot Tracks (approval tests - to be replaced later by unit tests) render
<div
class="post "
data-pid="61e19a3f078b4c9934e72ce4"
data-score="0"
data-score="1"
data-order=""
data-initialpid=""
data-loved=""
Expand Down Expand Up @@ -663,10 +665,10 @@ exports[`Hot Tracks (approval tests - to be replaced later by unit tests) render
<div class="stats">
<span
class="nbReposts"
style="display:none;"
style=""
onclick="javascript:showReposts('61e19a3f078b4c9934e72ce4')"
>
<span>0</span>
<span>1</span>
</span>
<span
class="nbLoves"
Expand Down Expand Up @@ -825,20 +827,22 @@ var JAMENDO_CLIENT_ID = "2c9a11b9";
</html>"
`;
exports[`Hot Tracks (approval tests - to be replaced later by unit tests) renders ranked tracks, starting at a given index 1`] = `
exports[`Hot Tracks (approval tests - to be replaced later by unit tests) renders ranked posts, starting at a given index 1`] = `
"{
hasMore: false,
tracks: [
{
_id: '61e19a3f078b4c9934e72ce4',
name: 'a regular track',
nbR: 1,
pId: '61e19a3f078b4c9934e72ce4',
score: 1
}
]
}"
`;
exports[`Hot Tracks (approval tests - to be replaced later by unit tests) renders ranked tracks, starting at a given index 2`] = `
exports[`Hot Tracks (approval tests - to be replaced later by unit tests) renders ranked posts, starting at a given index 2`] = `
" <div
class="post "
data-pid="61e19a3f078b4c9934e72ce4"
Expand Down Expand Up @@ -883,10 +887,10 @@ exports[`Hot Tracks (approval tests - to be replaced later by unit tests) render
<div class="stats">
<span
class="nbReposts"
style="display:none;"
style=""
onclick="javascript:showReposts('61e19a3f078b4c9934e72ce4')"
>
<span>0</span>
<span>1</span>
</span>
<span
class="nbLoves"
Expand Down Expand Up @@ -937,108 +941,3 @@ exports[`Hot Tracks (approval tests - to be replaced later by unit tests) render
</div>
"
`;
exports[`Hot Tracks (approval tests - to be replaced later by unit tests) updates the score of a track when it's liked 1`] = `
"{
hasMore: false,
tracks: [
{
_id: '__posted_track_id__',
eId: '/yt/track_A',
img: null,
lov: [
'61e19a3f078b4c9934e72ce2'
],
name: null,
nbL: 1,
nbP: 0,
nbR: 1,
rankIncr: null,
score: 100,
text: '',
trackId: '61e19a3f078b4c9934e72ce6',
trackUrl: '//youtube.com/watch?v=track_A',
uId: '61e19a3f078b4c9934e72ce1',
uNm: 'user 0'
},
{
_id: '61e19a3f078b4c9934e72ce7',
eId: '/yt/track_B',
score: 0,
trackUrl: '//youtube.com/watch?v=track_B'
}
]
}"
`;
exports[`Hot Tracks (approval tests - to be replaced later by unit tests) updates the score of a track when it's liked 2`] = `
"[
{
_id: new ObjectId("61e19a3f078b4c9934e72ce6"),
eId: '/yt/track_A',
img: null,
name: null,
nbL: 1,
nbP: 0,
nbR: 1,
pId: new ObjectId("__posted_track_id__"),
score: 100
},
{
_id: new ObjectId("61e19a3f078b4c9934e72ce7"),
eId: '/yt/track_B',
score: 0
}
]"
`;
exports[`Hot Tracks (approval tests - to be replaced later by unit tests) updates the score of a track when it's reposted 1`] = `
"{
hasMore: false,
tracks: [
{
_id: '__posted_track_id__',
eId: '/yt/track_A',
img: null,
name: null,
nbL: 0,
nbP: 0,
nbR: 2,
rankIncr: null,
score: 200,
text: '',
trackId: '61e19a3f078b4c9934e72ce6',
trackUrl: '//youtube.com/watch?v=track_A',
uId: '61e19a3f078b4c9934e72ce1',
uNm: 'user 0'
},
{
_id: '61e19a3f078b4c9934e72ce7',
eId: '/yt/track_B',
score: 0,
trackUrl: '//youtube.com/watch?v=track_B'
}
]
}"
`;
exports[`Hot Tracks (approval tests - to be replaced later by unit tests) updates the score of a track when it's reposted 2`] = `
"[
{
_id: new ObjectId("61e19a3f078b4c9934e72ce6"),
eId: '/yt/track_A',
img: null,
name: null,
nbL: 0,
nbP: 0,
nbR: 2,
pId: new ObjectId("__posted_track_id__"),
score: 200
},
{
_id: new ObjectId("61e19a3f078b4c9934e72ce7"),
eId: '/yt/track_B',
score: 0
}
]"
`;
Loading

0 comments on commit 7b870c3

Please sign in to comment.