Recsys: add flow and fix uncovered issues

This commit is contained in:
infinite-persistence 2022-05-24 14:52:54 +08:00
parent d5c964c208
commit e3e675455a
No known key found for this signature in database
GPG key ID: B9C3252EDC3D0AA0
4 changed files with 71 additions and 21 deletions

View file

@ -15,6 +15,7 @@
[options] [options]
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue suppress_comment=\\(.\\|\n\\)*\\$FlowIssue
suppress_comment=\\(.\\|\n\\)*\\$FlowIgnore
module.name_mapper='^constants\(.*\)$' -> '<PROJECT_ROOT>/ui/constants\1' module.name_mapper='^constants\(.*\)$' -> '<PROJECT_ROOT>/ui/constants\1'
module.name_mapper='^contexts\(.*\)$' -> '<PROJECT_ROOT>/ui/contexts\1' module.name_mapper='^contexts\(.*\)$' -> '<PROJECT_ROOT>/ui/contexts\1'
module.name_mapper='^util\(.*\)$' -> '<PROJECT_ROOT>/ui/util\1' module.name_mapper='^util\(.*\)$' -> '<PROJECT_ROOT>/ui/util\1'

View file

@ -1,3 +1,4 @@
// @flow
import { RECSYS_ENDPOINT } from 'config'; import { RECSYS_ENDPOINT } from 'config';
import { selectUser } from 'redux/selectors/user'; import { selectUser } from 'redux/selectors/user';
import { makeSelectRecommendedRecsysIdForClaimId } from 'redux/selectors/search'; import { makeSelectRecommendedRecsysIdForClaimId } from 'redux/selectors/search';
@ -10,6 +11,7 @@ import { makeSelectClaimForUri } from 'redux/selectors/claims';
import { selectPlayingUri, selectPrimaryUri } from 'redux/selectors/content'; import { selectPlayingUri, selectPrimaryUri } from 'redux/selectors/content';
import { selectClientSetting, selectDaemonSettings } from 'redux/selectors/settings'; import { selectClientSetting, selectDaemonSettings } from 'redux/selectors/settings';
import { selectIsSubscribedForClaimId } from 'redux/selectors/subscriptions'; import { selectIsSubscribedForClaimId } from 'redux/selectors/subscriptions';
// $FlowFixMe: cannot resolve..
import { history } from 'ui/store'; import { history } from 'ui/store';
const recsysEndpoint = RECSYS_ENDPOINT; const recsysEndpoint = RECSYS_ENDPOINT;
@ -28,7 +30,7 @@ const getClaimIdsFromUris = (uris) => {
: []; : [];
}; };
const recsys = { const recsys: Recsys = {
entries: {}, entries: {},
debug: false, debug: false,
/** /**
@ -36,23 +38,9 @@ const recsys = {
* Entries are Created either when recommendedContent loads, or when recommendedContent is clicked. * Entries are Created either when recommendedContent loads, or when recommendedContent is clicked.
* If recommended content is clicked, An Entry with parentUuid is created. * If recommended content is clicked, An Entry with parentUuid is created.
* On page load, find an empty entry with your claimId, or create a new entry and record to it. * On page load, find an empty entry with your claimId, or create a new entry and record to it.
* The entry will be populated with the following:
* - parentUuid // optional
* - Uuid
* - claimId
* - recommendedClaims [] // optionally empty
* - playerEvents [] // optionally empty
* - recommendedClaimsIndexClicked [] // optionally empty
* - UserId
* - pageLoadedAt
* - isEmbed
* - pageExitedAt
* - autoplay
* - recsysId // optional
*/ */
/** /**
* Function: onClickedRecommended()
* Called when RecommendedContent was clicked. * Called when RecommendedContent was clicked.
* Adds index of clicked recommendation to parent entry * Adds index of clicked recommendation to parent entry
* Adds new Entry with parentUuid for destination page * Adds new Entry with parentUuid for destination page
@ -61,20 +49,23 @@ const recsys = {
*/ */
onClickedRecommended: function (parentClaimId, newClaimId) { onClickedRecommended: function (parentClaimId, newClaimId) {
const parentEntry = recsys.entries[parentClaimId] ? recsys.entries[parentClaimId] : null; const parentEntry = recsys.entries[parentClaimId] ? recsys.entries[parentClaimId] : null;
const parentUuid = parentEntry['uuid']; const parentUuid = parentEntry ? parentEntry['uuid'] : '';
const parentRecommendedClaims = parentEntry['recClaimIds'] || []; const parentRecommendedClaims = parentEntry ? parentEntry['recClaimIds'] : [];
const parentClickedIndexes = parentEntry['recClickedVideoIdx'] || []; const parentClickedIndexes = parentEntry ? parentEntry['recClickedVideoIdx'] : [];
const indexClicked = parentRecommendedClaims.indexOf(newClaimId); const indexClicked = parentRecommendedClaims.indexOf(newClaimId);
if (parentUuid) { if (parentUuid) {
recsys.createRecsysEntry(newClaimId, parentUuid); recsys.createRecsysEntry(newClaimId, parentUuid);
} }
parentClickedIndexes.push(indexClicked); parentClickedIndexes.push(indexClicked);
recsys.log('onClickedRecommended', { parentClaimId, newClaimId }); // recsys.log('onClickedRecommended', { parentClaimId, newClaimId });
recsys.log('onClickedRecommended', newClaimId);
}, },
/** /**
* Page was loaded. Get or Create entry and populate it with default data, plus recommended content, recsysId, etc. * Page was loaded. Get or Create entry and populate it with default data,
* plus recommended content, recsysId, etc.
* Called from recommendedContent component * Called from recommendedContent component
*/ */
onRecsLoaded: function (claimId, uris, uuid = '') { onRecsLoaded: function (claimId, uris, uuid = '') {
@ -86,6 +77,13 @@ const recsys = {
const claimIds = getClaimIdsFromUris(uris); const claimIds = getClaimIdsFromUris(uris);
recsys.entries[claimId]['recsysId'] = makeSelectRecommendedRecsysIdForClaimId(claimId)(state) || recsysId; recsys.entries[claimId]['recsysId'] = makeSelectRecommendedRecsysIdForClaimId(claimId)(state) || recsysId;
recsys.entries[claimId]['pageLoadedAt'] = Date.now(); recsys.entries[claimId]['pageLoadedAt'] = Date.now();
// It is possible that `claimIds` include `null | undefined` entries
// instead of all being strings. I don't know if we should filter it,
// or change the `recClaimIds` definition. Leaving as is for now since
// any changes could affect existing recsys data set.
// -----------
// $FlowFixMe:
recsys.entries[claimId]['recClaimIds'] = claimIds; recsys.entries[claimId]['recClaimIds'] = claimIds;
} }
recsys.log('onRecsLoaded', claimId); recsys.log('onRecsLoaded', claimId);
@ -104,6 +102,7 @@ const recsys = {
const userId = user ? user.id : null; const userId = user ? user.id : null;
// Make a stub entry that will be filled out on page load // Make a stub entry that will be filled out on page load
// $FlowIgnore: not everything is defined since this is a stub
recsys.entries[claimId] = { recsys.entries[claimId] = {
uuid: uuid || Uuidv4(), uuid: uuid || Uuidv4(),
claimId: claimId, claimId: claimId,
@ -115,10 +114,13 @@ const recsys = {
}; };
if (parentUuid) { if (parentUuid) {
// $FlowFixMe: 'uid' should be a number, not null.
recsys.entries[claimId].uid = userId || null; recsys.entries[claimId].uid = userId || null;
recsys.entries[claimId].parentUuid = parentUuid; recsys.entries[claimId].parentUuid = parentUuid;
} else { } else {
// $FlowFixMe: 'uid' should be a number, not null.
recsys.entries[claimId].uid = userId; recsys.entries[claimId].uid = userId;
// $FlowFixMe: 'recsysId' should be a number, not null.
recsys.entries[claimId].recsysId = null; recsys.entries[claimId].recsysId = null;
recsys.entries[claimId].recClaimIds = []; recsys.entries[claimId].recClaimIds = [];
} }
@ -249,7 +251,7 @@ const recsys = {
const state = window.store.getState(); const state = window.store.getState();
const playingUri = selectPlayingUri(state); const playingUri = selectPlayingUri(state);
const actualPlayingUri = playingUri && playingUri.uri; const actualPlayingUri = playingUri && playingUri.uri;
const claim = makeSelectClaimForUri(actualPlayingUri)(state); const claim = makeSelectClaimForUri(actualPlayingUri || '')(state);
const playingClaimId = claim ? claim.claim_id : null; const playingClaimId = claim ? claim.claim_id : null;
// const primaryUri = selectPrimaryUri(state); // const primaryUri = selectPrimaryUri(state);
const floatingPlayer = selectClientSetting(state, SETTINGS.FLOATING_PLAYER); const floatingPlayer = selectClientSetting(state, SETTINGS.FLOATING_PLAYER);

2
flow-typed/Claim.js vendored
View file

@ -58,6 +58,8 @@ declare type GenericClaim = {
}, },
}; };
declare type ClaimId = string;
declare type GenericMetadata = { declare type GenericMetadata = {
title?: string, title?: string,
description?: string, description?: string,

45
flow-typed/recsys.js vendored Normal file
View file

@ -0,0 +1,45 @@
declare type Recsys = {
entries: { [ClaimId]: RecsysEntry },
debug: boolean,
onClickedRecommended: (parentClaimId: ClaimId, newClaimId: ClaimId) => void,
onRecsLoaded: (claimId: ClaimId, uris: Array<string>, uuid: string) => void,
createRecsysEntry: (claimId: ClaimId, parentUuid?: ?string, uuid?: string) => void,
sendRecsysEntry: (claimId: ClaimId, isTentative?: boolean) => ?Promise<?Response>,
onRecsysPlayerEvent: (claimId: ClaimId, event: RecsysPlaybackEvent, isEmbedded: boolean) => void,
log: (callName: string, claimId: ClaimId) => void,
onPlayerDispose: (claimId: ClaimId, isEmbedded: boolean, totalPlayingTime: number) => void,
onNavigate: () => void,
};
declare type RecsysEntry = {
uuid: string,
parentUuid?: string,
claimId: string,
uid?: number,
deviceId?: number,
pageLoadedAt: number, // UNIX timestamp (in UTC)
pageExitedAt: number, // UNIX timestamp (in UTC)
events: Array<RecsysPlaybackEvent>,
recsysId: string, // Recommender that produced recs
recClaimIds: Array<string>, // Recommendations
recClickedVideoIdx: Array<number>, // Video clicked index
isEmbed: boolean,
remoteIp: any, // [bytes] Caller IP address
tentative: boolean, // Visibility change rather than tab close
autoplay: boolean, // Was the last human action before this?
commentPulls: number, // How many comment pull calls did the user request?
recorded_at: number,
user_agent: string,
accept_lang: string,
tokenVerified: boolean,
totalPlayTime: number,
finalPlayPosition: number,
incognito: number, // User not logged in.
isResumedSend: boolean, // Data sent after browser is re-opened.
};
declare type RecsysPlaybackEvent = {
event: number, // 0 = start, 1 = stop, 2 = scrub, 3 = speed, 4 = end_of_play
offset: number, // Where playback was at time of event
arg: number,
};