File: //opt/proundcontra.mosmuc.de/statisticsMgr.php
<?php
class StatisticsMgr
{
public function __construct()
{
$this->queryCacheHits = 0;
$this->queryCacheMisses = 0;
$this->run();
}
/*
* run all necessary statistics
*/
public function run()
{
// Currently empty, but here for future expansion
}
public function queryCacheHit()
{
$this->queryCacheHits++;
}
public function queryCacheMiss()
{
$this->queryCacheMisses++;
}
public function queryCacheHits()
{
return $this->queryCacheHits;
}
public function queryCacheMisses()
{
return $this->queryCacheMisses;
}
public function updateQuestionStats($questionId)
{
global $sDB, $sTimer;
error_log("update question stats for question $questionId");
$sTimer->start("updateQuestionStats");
$res = $sDB->exec("
SELECT *
FROM `questions`
WHERE `questionId` = '".i($questionId)."'
LIMIT 1;
");
if (!mysqli_num_rows($res)) {
return false;
}
$q = mysqli_fetch_object($res);
$this->updateQuestionScore($questionId);
$this->updateArgumentScoreBatch($questionId);
$sTimer->stop("updateQuestionStats");
}
private function trendingScore($score, $dateAdded)
{
$timePassed = $dateAdded - 1338501600;
$s = 0;
if ($score > 0) {
$s = 1;
} else if ($score < 0) {
$s = -1;
}
$ret = round(log(max(abs($score), 1), 10) + $s * $timePassed / 45000);
return $ret;
}
private function updateQuestionScore($questionId)
{
global $sDB, $sTimer;
$sTimer->start("updateQuestionScore");
$res = $sDB->exec("
SELECT *
FROM `questions`
WHERE `questionId` = '".i($questionId)."'
LIMIT 1;
");
if (!mysqli_num_rows($res)) {
return false;
}
$q = mysqli_fetch_object($res);
$num = [
VOTE_UP => 0,
VOTE_DN => 0
];
$res = $sDB->exec("
SELECT count(*) as `cnt`, `vote`
FROM `user_votes`
WHERE `questionId` = '".i($questionId)."'
AND `argumentId` = '0'
GROUP BY `vote`;
");
while ($row = mysqli_fetch_object($res)) {
$num[$row->vote] = $row->cnt;
}
$score = $num[VOTE_UP] - $num[VOTE_DN];
$scoreTop = $score;
$scoreTrending = $this->trendingScore($score, $q->dateAdded);
$oldScore = $q->score;
error_log("update question score for $questionId: $oldScore -> $score");
$sDB->exec("
UPDATE `questions`
SET `score` = '".i($score)."',
`scoreTrending` = '".i($scoreTrending)."',
`scoreTop` = '".i($scoreTop)."'
WHERE `questionId` = '".i($questionId)."'
LIMIT 1;
");
$sTimer->stop("updateQuestionScore");
}
/*
* batch update argument scores for all arguments of the given question.
*/
private function updateArgumentScoreBatch($questionId)
{
global $sDB, $sTimer;
$sTimer->start("updateArgumentScoreBatch");
$sTimer->start("updateArgumentScoreBatch::Fetch");
$score = [];
$res = $sDB->exec("
SELECT count(*) as `cnt`, `vote`, `argumentId`
FROM `user_votes`
WHERE `questionId` = '".i($questionId)."'
GROUP BY `argumentId`, `vote`;
");
while ($row = mysqli_fetch_object($res)) {
if ($row->argumentId == 0) {
continue;
}
if (!isset($score[$row->argumentId])) {
$score[$row->argumentId] = 0;
}
// The 'vote' field is presumably 1 or -1, multiplied by the count
$score[$row->argumentId] += $row->vote * $row->cnt;
}
$sTimer->stop("updateArgumentScoreBatch::Fetch");
$sTimer->start("updateArgumentScoreBatch::Update");
$sDB->exec("START TRANSACTION;");
foreach ($score as $k => $v) {
$sDB->exec("
UPDATE `arguments`
SET `score` = '".i($v)."'
WHERE `questionId` = '".i($questionId)."'
AND `argumentId` = '".i($k)."'
LIMIT 1;
");
}
$sDB->exec("COMMIT;");
$sTimer->stop("updateArgumentScoreBatch::Update");
$sTimer->stop("updateArgumentScoreBatch");
}
private function updateArgumentScore($questionId, $argumentId)
{
global $sDB, $sTimer;
$num = [
VOTE_UP => 0,
VOTE_DN => 0
];
$res = $sDB->exec("
SELECT count(*) as `cnt`, `vote`
FROM `user_votes`
WHERE `questionId` = '".i($questionId)."'
AND `argumentId` = '".i($argumentId)."'
GROUP BY `vote`;
");
while ($row = mysqli_fetch_object($res)) {
$num[$row->vote] = $row->cnt;
}
$score = $num[VOTE_UP] - $num[VOTE_DN];
$sDB->exec("
UPDATE `arguments`
SET `score` = '".i($score)."'
WHERE `questionId` = '".i($questionId)."'
AND `argumentId` = '".i($argumentId)."'
LIMIT 1;
");
}
/*
* Remove all votes from this argument.
*/
public function resetArgumentVotes(Argument $argument)
{
global $sDB;
$inc = 0;
$res = $sDB->exec("
SELECT *
FROM `user_votes`
WHERE `argumentId` = '".i($argument->argumentId())."';
");
while ($row = mysqli_fetch_object($res)) {
$inc += $row->vote;
}
$sDB->execUsers("
UPDATE `users`
SET `scoreArguments` = `scoreArguments` - ".i($inc)."
WHERE `userId` = '".i($argument->userId())."'
LIMIT 1;
");
$sDB->exec("
DELETE FROM `user_votes`
WHERE `argumentId` = '".i($argument->argumentId())."';
");
}
/*
* Remove all votes from this question.
* Make sure that no arguments exist before this call!
*/
public function resetQuestionVotes(Question $question)
{
global $sDB;
$inc = 0;
$res = $sDB->exec("
SELECT *
FROM `user_votes`
WHERE `questionId` = '".i($question->questionId())."';
");
while ($row = mysqli_fetch_object($res)) {
$inc += $row->vote;
}
$sDB->execUsers("
UPDATE `users`
SET `scoreQuestions` = `scoreQuestions` - ".i($inc)."
WHERE `userId` = '".i($question->authorId())."'
LIMIT 1;
");
$sDB->exec("
DELETE FROM `user_votes`
WHERE `questionId` = '".i($question->questionId())."';
");
}
public function vote(Question $question, $argumentId, $vote, $user = false, $forceVote = false)
{
global $sUser, $sDB, $sQuery, $sPermissions;
$questionId = $question->questionId();
if (!$user) {
$user = $sUser;
if (!$sUser->isLoggedIn() && $question->type() != QUESTION_TYPE_UNLISTED) {
return false;
}
}
if (!in_array($vote, [VOTE_UP, VOTE_DN, VOTE_NONE])) {
return false;
}
if ($sPermissions->getPermission($user, ACTION_VOTE) == PERMISSION_DISALLOWED) {
return false;
}
if (!$user->isLoggedIn()) {
return false;
}
$this->lazyUpdateUserStats($questionId, $argumentId, $vote, $user->getUserId());
$sDB->exec("
DELETE FROM `user_votes`
WHERE `userId` = '".i($user->getUserId())."'
AND `questionId` = '".i($questionId)."'
AND `argumentId` = '".i($argumentId)."';
");
if ($vote != VOTE_NONE) {
$sDB->exec("
INSERT INTO `user_votes`
(`voteId`, `userId`, `questionId`, `argumentId`, `vote`, `dateAdded`)
VALUES
(NULL,
'".i($user->getUserId())."',
'".i($questionId)."',
'".i($argumentId)."',
'".i($vote)."',
'".time()."');
");
}
if ($argumentId) {
$this->updateArgumentScore($questionId, $argumentId);
} else {
$this->updateQuestionStats($questionId);
}
return true;
}
/*
* Update user score lazily.
* After this call, user stats are updated such that the vote of userId is taken into account.
*/
private function lazyUpdateUserStats($questionId, $argumentId, $vote, $userId)
{
global $sUser, $sDB, $sQuery;
if ($sUser->getUserId() == 0) {
return;
}
$res = $sDB->exec("
SELECT *
FROM `user_votes`
WHERE `userId` = '".i($sUser->getUserId())."'
AND `questionId` = '".i($questionId)."'
AND `argumentId` = '".i($argumentId)."';
");
$inc = $vote;
while ($row = mysqli_fetch_object($res)) {
$inc -= $row->vote;
}
$authorId = $sQuery->getAuthorById($questionId, $argumentId);
if ($argumentId) {
$sDB->execUsers("
UPDATE `users`
SET `scoreArguments` = `scoreArguments` + ".i($inc)."
WHERE `userId` = '".i($authorId)."'
LIMIT 1;
");
} else {
$sDB->execUsers("
UPDATE `users`
SET `scoreQuestions` = `scoreQuestions` + ".i($inc)."
WHERE `userId` = '".i($authorId)."'
LIMIT 1;
");
}
}
private $queryCacheHits;
private $queryCacheMisses;
}
?>