Open xavxyz opened 8 years ago
Hi, willing to work on this. Questions: Where do I have to create the callbacks(pieces of scoring.js)? To break down into pieces, can somebody provide me some guidelines, group some logic together etc?
Hey @S4KH!
You can create the different functions in scoring.js
๐
The function Telescope.updateScore
takes an object with 3 keys and is for instance called like that:
// post is a post document
Telescope.updateScore({collection: Posts, item: post, forceUpdate: true})
It is called when clicking an down/upvote button or in a cron (see nova-voting/lib/server/cron.js). -> If the function is called with forceUpdate, the return value doesn't matter, we want the function to update the collection's item with a new score (click a button). -> If the function is called without forceUpdate, the return value corresponds to the value added to the existing score (the update takes place in the cron)
Currently, the structure of this function is :
Telescope.updateScore = ({ collection, item, forceUpdate }) => {
// status check
// age check (not published or scheduled in the future)
// power algorithm
// get a score
// get a score diff (absolute value)
// update item score : if forceUpdate is true or the score diff is relevant (not too small) and set it to active
// or if it's an old item set it to inactive and don't touch its score
}
The first checks & the power algorithms could be default callbacks that we provide. A callback function will take at least two arguments, the current score being modified and the item, and will return a new score.
To write a callback have a look at nova-posts/lib/callbacks.js
, nova-posts/lib/parameters.js
(this one is a smaller file and may be easier to read through), nova-comments/lib/callbacks.js
.
We would do Telescope.callbacks.add("scoring.all", checkPostDate);
to add a function.
And to get a new score inside the "big" function: const newScore = Telescope.callbacks.run("scoring.all");
. After that being the diff and updates
Is it clearer? ๐ฎ
edit: oops wrong button, sorry for the closing! ๐
Thanks for the quick response :+1:
I was wondering will all the sections of the current updateScore
break down into callbacks?
// status check
// age check (not published or scheduled in the future)
// power algorithm
// get a score
// get a score diff (absolute value)
// update item score : if forceUpdate is true or the score diff is relevant (not too small) and set it to active
// or if it's an old item set it to inactive and don't touch its score`
If yes, what are the suitable namings of them?
Good question, only the first 3 parts should be broken in callbacks! ๐
Find naming that feels right, in ~camelCase, maybe something like ScoreStatusCheck
, ScoreAgeCheck
, ..
:clap: for the quick reply. Sorry for posting the entire file. Just asking if I am doing correctly. Btw when I press login to view the username password inputs nothing is displayed inside the modal.
Did something like this:
import Telescope from 'meteor/nova:lib';
Telescope.updateScore = function (args) {
const collection = args.collection;
const item = args.item;
const forceUpdate = args.forceUpdate;
// console.log(item)
// Status Check
// Age Check
// For performance reasons, the database is only updated if the difference between the old score and the new score
// is meaningful enough. To find out, we calculate the "power" of a single vote after n days.
// We assume that after n days, a single vote will not be powerful enough to affect posts' ranking order.
// Note: sites whose posts regularly get a lot of votes can afford to use a lower n.
//power algo
// console.log(now)
// console.log(age)
// console.log(ageInHours)
// console.log(baseScore)
// console.log(newScore)
const newScore = Telescope.callbacks.run("scoring.all");
// Note: before the first time updateScore runs on a new item, its score will be at 0
const scoreDiff = Math.abs(item.score - newScore);
// only update database if difference is larger than x to avoid unnecessary updates
if (forceUpdate || scoreDiff > x){
collection.update(item._id, {$set: {score: newScore, inactive: false}});
return 1;
}else if(ageInHours > n*24){
// only set a post as inactive if it's older than n days
collection.update(item._id, {$set: {inactive: true}});
}
return 0;
};
// ------------------------------------- scoring.all -------------------------------- //
/**
* @summary Check if item is scorable based on its status
*/
function ItemStatusCheck (item) {
if (!!item.status && item.status !== 2) // if item has a status and is not approved, don't update its score
return 0;
return 1;
}
Telescope.callbacks.add("scoring.all", ItemStatusCheck);
/**
* @summary Check if item is scorable based on its age
*/
function ItemAgeCheck (item) {
// If for some reason item doesn't have a "postedAt" property, abort
if (!item.postedAt)
return 0;
const postedAt = item.postedAt.valueOf();
const now = new Date().getTime();
const age = now - postedAt;
const ageInHours = age / (60 * 60 * 1000);
if (postedAt > now) // if post has been scheduled in the future, don't update its score
return 0;
return ageInHours;
}
Telescope.callbacks.add("scoring.all", ItemAgeCheck);
/**
* @summary PowerAlgorithm
*/
function PowerAlgorithm (item, ageInHours) {
// n = number of days after which a single vote will not have a big enough effect to trigger a score update
// and posts can become inactive
const n = 30;
// x = score increase amount of a single vote after n days (for n=100, x=0.000040295)
const x = 1/Math.pow(n*24+2,1.3);
// time decay factor
const f = 1.3;
// use baseScore if defined, if not just use 0
const baseScore = item.baseScore || 0;
// HN algorithm
const newScore = baseScore / Math.pow(ageInHours + 2, f);
return newScore;
}
Telescope.callbacks.add("scoring.all", PowerAlgorithm);
For the problem with the dropdown, checkout on devel
and run npm install
again, it's a problem caused by a bug in latest version of react-bootstrap
.
On a global view, we are close to it! However, beware of the scope of the variables like x
or ageInHours
, they are defined in some function and used in others in what you pasted. Also, the callbacks should only take the score
as an argument and return a newScore. It could be init as const score = item.baseScore || 0
before running the callbacks.
The sad thing is still can't login :cry:. Followed your instructions still can't login.
On your fork, this line causes trouble: https://github.com/S4KH/Telescope/blob/856a0c43397f026018576d41047e923ce39a5347/package.json#L26
Do a git pull
to get the latest ๐
This is a starter issue, highly inspired by the great job of the folks at Hoodie. ๐
๐๐๐๐ญ๐ป Hacktoberfest: Trick or Treat!
If you havenโt yet, sign up for Hacktoberfest to earn an exclusive T-Shirt.
We are sure you can learn a cool trick or two in the process on how to hack & customize Telescope Nova! ๐ญ
๐ค What you will need to know
Meteor, React, Telescope Nova callbacks handling (video).
Most of the Telescope Nova structure is extensible. A big part of that is done thanks to
callbacks
as Nova uses a system of successive callbacks to perform some action.The callback below is for example run on the client when a post is submitted:
๐ฏ The Goal
The voting algorithm is currently self-contained in
nova:voting/lib/scoring.js
(code)As suggested in an old issue (two years ago! #421), this would be awesome to be able to make our own algorithm without modifying the core and from our custom package.
A simple set of callbacks is the one for filtering posts view:
postsParameters
.This can be a good starting point to take inspiration to create callbacks for
Telescope.updateScore
function :"scoring.all"
callbacks.The current
Telescope.updateScore
content would be broken in several functions, these functions being then added as callbacks. These callbacks would be created and run likely the other Telescope Nova's callbacks.All these callbacks would be added thanks to
Telescope.callbacks.add("scoring.all", myAlgoExtension)
and run insideTelescope.updateScore
with something like:Nice to have: We could have a general algorithm (
"scoring.all"
) and some specific algorithm forposts
orcomments
:"scoring.posts"
&"scoring.comments"
. This would be based on thecollection
argument passed to the functionTelescope.updateScore
.Nice to have: This would also be the occasion to update this package to ES6 syntax.
Nice to have: We could also have an example in the
my-custom-package
example.๐ Step by Step
devel
branch.Telescope.updateScore
function's content in several functions (callbacks).posts
,comments
or whatever collection.my-custom-package
.devel
branch, mentioning this issue.Ping us here, in the Telescope Slack room or on Twitter if you have any questions ๐