_userid = $userid;
$this->_ratings_loaded = false;
$this->_ratings = array();
$this->_num_ratings = 0;
$this->_mean_ratings = array();
$this->_pearson_sims = array();
}
function getId()
{
return $this->_userid;
}
function & _get_rating_dbi()
{
// This is a hack, because otherwise this object doesn't know about a
// DBI at all. Perhaps all this ratings stuff should live somewhere
// else that's less of a base class.
if (isset($this->_rdbi))
return $this->_rdbi;
$this->_rdbi = RatingsDb::getTheRatingsDb();
return $this->_rdbi;
}
// XXX: may want to think about caching ratings in the PHP session
// since a WikiUser is created for *every* access, in which case rate.php
// will want to change to use this object instead of direct db access
/**
* Check whether $user is allowed to view this user's ratings
*
* @return bool True if $user can view this user's ratings, false otherwise
*/
function allow_view_ratings($user)
{
return true;
}
/**
* Gets this user's ratings
*
* @return array Assoc. array [page_name][dimension] = _UserRating object
*/
function get_ratings()
{
$this->_load_ratings();
return $this->_ratings;
}
/**
* Gets this user's mean rating across a dimension
*
* @return float Mean rating
*/
function mean_rating($dimension = 0)
{
// use memoized result if available
if (isset($this->_mean_ratings[$dimension])) {
return $this->_mean_ratings[$dimension];
}
$ratings = $this->get_ratings();
$total = 0;
$n = 0;
// walk the ratings and aggregate those in this dimension
foreach ($ratings as $page => $rating) {
if (isset($rating[$dimension])) {
$total += $rating[$dimension]->get_rating();
$n++;
}
}
// memoize and return result
$this->_mean_ratings[$dimension] = ($n == 0 ? 0 : $total / $n);
return $this->_mean_ratings[$dimension];
}
// Note: the following has_rated, get_rating, set_rating, and unset_rating
// methods are colossally inefficient as they do a full ratings load from
// the database before performing their intended operation -- as such, the
// rate.php script still uses the direct database methods (plus, it's very
// ephemeral and doesn't particularly care about the ratings count or any
// other features that these methods might provide down the road)
function has_rated($pagename, $dimension = null)
{
// XXX: does this really want to do a full ratings load? (scalability?)
$this->_load_ratings();
if (isset($dimension)) {
if (isset($this->_ratings[$pagename][$dimension])) {
return true;
}
} else {
if (isset($this->_ratings[$pagename])) {
return true;
}
}
return false;
}
function get_rating($pagename, $dimension = 0)
{
// XXX: does this really want to do a full ratings load? (scalability?)
if (RATING_STORAGE == 'SQL')
$this->_load_ratings();
else {
$rdbi = $this->_get_rating_dbi();
return $rdbi->metadata_get_rating($this->getId(), $pagename, $dimension);
}
if ($this->has_rated($pagename, $dimension)) {
return $this->_ratings[$pagename][$dimension]->get_rating();
}
return false;
}
function set_rating($pagename, $dimension, $rating)
{
// XXX: does this really want to do a full ratings load? (scalability?)
$this->_load_ratings();
// XXX: what to do on failure?
$dbi = $this->_get_rating_dbi();
if (!($dbi->rate($this->_userid, $pagename, $dimension, $rating))) {
return;
}
if ($this->has_rated($pagename, $dimension)) {
$this->_ratings[$pagename][$dimension]->set_rating($rating);
} else {
$this->_num_ratings++;
$this->_ratings[$rating['pagename']][$rating['dimension']]
= new _UserRating($this->_userid, $pagename, $dimension, $rating);
}
}
function unset_rating($pagename, $dimension)
{
// XXX: does this really want to do a full ratings load? (scalability?)
$this->_load_ratings();
if ($this->has_rated($pagename, $dimension)) {
// XXX: what to do on failure?
if ($this->_dbi->delete_rating($this->_userid, $pagename, $dimension)) {
$this->_num_ratings--;
unset($this->_ratings[$pagename][$dimension]);
if (!count($this->_ratings[$pagename])) {
unset($this->_ratings[$pagename]);
}
}
}
}
function pearson_similarity($user, $dimension = 0)
{
// use memoized result if available
if (isset($this->_pearson_sims[$user->getId()][$dimension])) {
return $this->_pearson_sims[$user->getId()][$dimension];
}
$ratings1 = $this->get_ratings();
$mean1 = $this->mean_rating($dimension);
// XXX: sanify user input?
$ratings2 = $user->get_ratings();
$mean2 = $user->mean_rating($dimension);
// swap if it would speed things up a bit
if (count($ratings1) < count($ratings2)) {
$tmp = $ratings1;
$ratings1 = $ratings2;
$ratings2 = $tmp;
$tmp = $mean1;
$mean1 = $mean2;
$mean2 = $tmp;
}
list($sum11, $sum22, $sum12, $n) = array(0, 0, 0, 0);
// compute sum(x*x), sum(y*y), sum(x*y) over co-rated items
foreach ($ratings1 as $page => $rating1) {
if (isset($rating1[$dimension]) && isset($ratings2[$page])) {
$rating2 = $ratings2[$page];
if (isset($rating2[$dimension])) {
$r1 = $rating1[$dimension]->get_rating();
$r2 = $rating2[$dimension]->get_rating();
// print "co-rating with " . $user->getId() . " $page $r1 $r2
";
$r1 -= $mean1;
$r2 -= $mean2;
$sum11 += $r1 * $r1;
$sum22 += $r2 * $r2;
$sum12 += $r1 * $r2;
$n++;
}
}
}
// this returns both the computed similarity and the number of co-rated
// items that the similarity was based on
// prevent division-by-zero
if (sqrt($sum11) == 0 || sqrt($sum12) == 0)
$sim = array(0, $n);
else
// Pearson similarity
$sim = array($sum12 / (sqrt($sum11) * sqrt($sum22)), $n);
// print "sim is " . $sim[0] . "
";
// memoize result
$this->_pearson_sims[$user->getId()][$dimension] = $sim;
return $this->_pearson_sims[$user->getId()][$dimension] = $sim;
}
function knn_uu_predict($pagename, &$neighbors, $dimension = 0)
{
/*
print "
"; var_dump($this->_pearson_sims); var_dump($this->_ratings); print ""; print "pred for $pagename