Ratioed: add statistics about reply likes and reply guy score

This commit is contained in:
Matthew Exon 2025-01-05 00:23:51 +01:00
parent 44d2079da2
commit 3441c9bd0e
3 changed files with 192 additions and 4 deletions

View file

@ -8,7 +8,9 @@ use Friendica\Core\Renderer;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\User;
use Friendica\Model\Verb;
use Friendica\Module\Moderation\Users\Active;
use Friendica\Protocol\Activity;
/**
* This class implements the "Behaviour" panel in Moderation/Users
@ -75,6 +77,11 @@ class RatioedPanel extends Active
$this->t('Comments last 24h'),
$this->t('Reactions last 24h'),
$this->t('Ratio last 24h'),
$this->t('Replies last month'),
$this->t('Reply likes'),
$this->t('Respondee likes'),
$this->t('OP likes'),
$this->t('Reply guy score'),
];
$field_names = [
'name',
@ -87,6 +94,11 @@ class RatioedPanel extends Active
'comments',
'reactions',
'ratio',
'reply_count',
'reply_likes',
'reply_respondee_likes',
'reply_op_likes',
'reply_guy_score',
];
$th_users = array_map(null, $header_titles, $valid_orders, $field_names);
@ -125,6 +137,129 @@ class RatioedPanel extends Active
]);
}
protected function getReplyGuyRow($contact_uid)
{
$like_vid = Verb::getID(Activity::LIKE);
$post_vid = Verb::getID(Activity::POST);
/*
* This is a complicated query.
*
* The innermost select retrieves a chain of four posts: an
* original post, a target comment (possibly deep down in the
* thread), a reply from our user, and a like for that reply.
* If there's no like, we still want to count the reply, so we
* use an outer join.
*
* The second select adds "points" for different kinds of
* likes. The outermost select then counts up these points,
* and the number of distinct replies.
*/
$reply_guy_result = DBA::p('
SELECT
COUNT(distinct reply_id) AS replies_total,
SUM(like_point) AS like_total,
SUM(target_like_point) AS target_like_total,
SUM(original_like_point) AS original_like_total
FROM (
SELECT
reply_id,
like_date,
like_date IS NOT NULL AS like_point,
like_author = target_author AS target_like_point,
like_author = original_author AS original_like_point
FROM (
SELECT
original_post.`uri-id` AS original_id,
original_post.`author-id` AS original_author,
original_post.created AS original_date,
target_post.`uri-id` AS target_id,
target_post.`author-id` AS target_author,
target_post.created AS target_date,
reply_post.`uri-id` AS reply_id,
reply_post.`author-id` AS reply_author,
reply_post.created AS reply_date,
like_post.`uri-id` AS like_id,
like_post.`author-id` AS like_author,
like_post.created AS like_date
FROM
post AS original_post
JOIN
post AS target_post
ON
original_post.`uri-id` = target_post.`parent-uri-id`
JOIN
post AS reply_post
ON
target_post.`uri-id` = reply_post.`thr-parent-id` AND
reply_post.`author-id` = ? AND
reply_post.`author-id` != target_post.`author-id` AND
reply_post.`author-id` != original_post.`author-id` AND
reply_post.`uri-id` != reply_post.`thr-parent-id` AND
reply_post.vid = ? AND
reply_post.created > CURDATE() - INTERVAL 1 MONTH
LEFT OUTER JOIN
post AS like_post
ON
reply_post.`uri-id` = like_post.`thr-parent-id` AND
like_post.vid = ? AND
like_post.`author-id` != reply_post.`author-id`
) AS post_meta
) AS reply_counts
', $contact_uid, $post_vid, $like_vid);
return $reply_guy_result;
}
// https://stackoverflow.com/a/48283297/235936
protected function sigFig($value, $digits)
{
if ($value == 0) {
$decimalPlaces = $digits - 1;
} elseif ($value < 0) {
$decimalPlaces = $digits - floor(log10($value * -1)) - 1;
} else {
$decimalPlaces = $digits - floor(log10($value)) - 1;
}
$answer = ($decimalPlaces > 0) ?
number_format($value, $decimalPlaces) : round($value, $decimalPlaces);
return $answer;
}
protected function fillReplyGuyData(&$user) {
$reply_guy_result = $this->getReplyGuyRow($user['user_contact_uid']);
if (DBA::isResult($reply_guy_result)) {
$reply_guy_result_row = DBA::fetch($reply_guy_result);
$user['reply_count'] = $reply_guy_result_row['replies_total'] ?? 0;
$user['reply_likes'] = $reply_guy_result_row['like_total'] ?? 0;
$user['reply_respondee_likes'] = $reply_guy_result_row['target_like_total'] ?? 0;
$user['reply_op_likes'] = $reply_guy_result_row['original_like_total'] ?? 0;
$denominator = $user['reply_likes'] + $user['reply_respondee_likes'] + $user['reply_op_likes'];
if ($user['reply_count'] == 0) {
$user['reply_guy'] = false;
$user['reply_guy_score'] = 0;
}
elseif ($denominator == 0) {
$user['reply_guy'] = true;
$user['reply_guy_score'] = '∞';
}
else {
$reply_guy_score = $user['reply_count'] / $denominator;
$user['reply_guy'] = $reply_guy_score >= 1.0;
$user['reply_guy_score'] = $this->sigFig($reply_guy_score, 2);
}
}
else {
$user['reply_count'] = "error";
$user['reply_likes'] = "error";
$user['reply_respondee_likes'] = "error";
$user['reply_op_likes'] = "error";
$user['reply_guy'] = false;
$user['reply_guy_score'] = 0;
}
}
protected function setupUserCallback(): \Closure
{
Logger::debug("ratioed: setupUserCallback");
@ -179,6 +314,8 @@ class RatioedPanel extends Active
$user['ratioed'] = false;
}
$this->fillReplyGuyData($user);
$user = $parentCallback($user);
Logger::debug("ratioed: setupUserCallback", [
'uid' => $user['uid'],