chevereto-free/app/lib/classes/class.stat.php

216 lines
11 KiB
PHP

<?php
/* --------------------------------------------------------------------
This file is part of Chevereto Free.
https://chevereto.com/free
(c) Rodolfo Berrios <rodolfo@chevereto.com>
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
--------------------------------------------------------------------- */
namespace CHV;
use G;
use Exception;
class Stat
{
public static function getTotals()
{
$totals = DB::queryFetchSingle('SELECT * FROM '.DB::getTable('stats').' WHERE stat_type = "total"');
unset($totals['stat_id'], $totals['stat_type'], $totals['date_gmt']);
return DB::formatRow($totals);
}
public static function track($args=[])
{
if (!in_array($args['action'], ['insert', 'update', 'delete'])) {
throw new StatException(sprintf('Invalid stat action "%s" in ', $args['action']) . __METHOD__, 100);
}
$tables = DB::getTables();
// Restrict unknown tables
if (!array_key_exists($args['table'], $tables)) {
throw new StatException(sprintf('Unknown table "%s" in ' . __METHOD__, $args['table']), 101);
}
// Restrict tables by stat action
switch ($args['action']) {
case 'insert':
if (!in_array($args['table'], ['albums', 'images', 'likes', 'users'])) {
throw new StatException(sprintf('Table "%s" does not bind an stat procedure in ' . __METHOD__, $args['table']), 101);
}
break;
}
// Check missing values
if ($args['table'] == 'images' and in_array($args['action'], ['insert', 'delete'])) {
if (!isset($args['disk_sum'])) {
$disk_sum_value = 0;
} else {
if (preg_match('/^([\+\-]{1})?\s*([\d]+)$/', $args['disk_sum'], $matches)) {
$disk_sum_value = $matches[2];
} else {
throw new StatException(sprintf('Invalid disk_sum value "%s" in ' . __METHOD__, $args['disk_sum']), 104);
}
}
}
// Fix values
if (!isset($args['value'])) {
$value = 1;
} else {
if (preg_match('/^([\+\-]{1})?\s*([\d]+)$/', $args['value'], $matches)) {
$value = $matches[2];
} else {
throw new StatException(sprintf('Invalid value "%s" in ' . __METHOD__, $args['value']), 102);
}
}
if (!isset($args['date_gmt'])) {
switch ($args['action']) {
case 'insert':
case 'update':
$args['date_gmt'] = G\datetimegmt();
break;
case 'delete':
throw new StatException('Missing date_gmt value in ' . __METHOD__, 105);
break;
}
} else {
try {
$date = new \DateTime($args['date_gmt']);
$args['date_gmt'] = $date->format('Y-m-d');
} catch (Exception $e) {
//throw new StatException($e);
throw new StatException('Invalid date_gmt value in ' . __METHOD__, 106);
}
}
// Get to the choppa
switch ($args['action']) {
case 'insert':
switch ($args['table']) {
case 'images':
if (!isset($args['disk_sum'])) {
throw new StatException('Missing disk_sum value in ' . __METHOD__, 103);
}
$sql_tpl =
'UPDATE `%table_stats` SET stat_images = stat_images + %value, stat_disk_used = stat_disk_used + %disk_sum WHERE stat_type = "total";' . "\n" .
'INSERT INTO `%table_stats` (stat_type, stat_date_gmt, stat_images, stat_disk_used) VALUES ("date",DATE("%date_gmt"),"%value", "%disk_sum") ON DUPLICATE KEY UPDATE stat_images = stat_images + %value, stat_disk_used = stat_disk_used + %disk_sum;';
break;
default: // albums, likes, users
$sql_tpl =
'UPDATE `%table_stats` SET stat_%related_table = stat_%related_table + %value WHERE stat_type = "total";' . "\n" .
'INSERT `%table_stats` (stat_type, stat_date_gmt, stat_%related_table) VALUES ("date",DATE("%date_gmt"),"%value") ON DUPLICATE KEY UPDATE stat_%related_table = stat_%related_table + %value;';
break;
}
break;
case 'update':
switch ($args['table']) {
case 'images':
case 'albums':
// Track image | album | user views
$sql_tpl =
'UPDATE `%table_stats` SET stat_%aux_views = stat_%aux_views + %value WHERE stat_type = "total";' . "\n" .
'INSERT INTO `%table_stats` (stat_type, stat_date_gmt, stat_%aux_views) VALUES ("date",DATE("%date_gmt"),"%value") ON DUPLICATE KEY UPDATE stat_%aux_views = stat_%aux_views + %value;';
if (isset($args['user_id'])) {
$sql_tpl .= "\n" . 'UPDATE `%table_users` SET user_content_views = user_content_views + %value WHERE user_id = %user_id;';
}
$sql_tpl = strtr($sql_tpl, ['%aux' => DB::getFieldPrefix($args['table'])]);
break;
}
break;
case 'delete':
switch ($args['table']) {
case 'images':
$sql_tpl =
'UPDATE `%table_stats` SET stat_images = GREATEST(stat_images - %value, 0) WHERE stat_type = "total";' . "\n" .
'UPDATE `%table_stats` SET stat_images = GREATEST(stat_images - %value, 0) WHERE stat_type = "date" AND stat_date_gmt = DATE("%date_gmt");' . "\n" .
'UPDATE `%table_stats` SET stat_image_likes = GREATEST(stat_image_likes - %likes, 0) WHERE stat_type = "total";' . "\n" .
'UPDATE `%table_stats` SET stat_image_likes = GREATEST(stat_image_likes - %likes, 0) WHERE stat_type = "date" AND stat_date_gmt = DATE("%date_gmt");' . "\n" .
'UPDATE `%table_stats` SET stat_disk_used = GREATEST(stat_disk_used - %disk_sum, 0) WHERE stat_type = "total";' . "\n" .
'UPDATE `%table_stats` SET stat_disk_used = GREATEST(stat_disk_used - %disk_sum, 0) WHERE stat_type = "date" AND stat_date_gmt = DATE("%date_gmt");';
break;
default: // albums, likes, users
$sql_tpl =
'UPDATE `%table_stats` SET stat_%related_table = GREATEST(stat_%related_table - %value, 0) WHERE stat_type = "total";' . "\n" .
'UPDATE `%table_stats` SET stat_%related_table = GREATEST(stat_%related_table - %value, 0) WHERE stat_type = "date" AND stat_date_gmt = DATE("%date_gmt");';
if ($args['table'] == 'users') {
$sql_tpl .=
// Update likes stats related to this deleted user
'UPDATE IGNORE `%table_stats` AS S
INNER JOIN (
SELECT DATE(like_date_gmt) AS like_date_gmt, COUNT(*) AS cnt
FROM `%table_likes`
WHERE like_user_id = %user_id
GROUP BY DATE(like_date_gmt)
) AS L ON S.stat_date_gmt = L.like_date_gmt
SET S.stat_image_likes = GREATEST(S.stat_image_likes - COALESCE(L.cnt, "0"), 0) WHERE stat_type = "date";
UPDATE IGNORE `%table_stats` SET stat_image_likes = GREATEST(stat_image_likes - COALESCE((SELECT COUNT(*) FROM `%table_likes` WHERE like_user_id = %user_id), "0"), 0) WHERE stat_type = "total";' . "\n" .
// Update album stats related to this deleted user
'UPDATE IGNORE `%table_stats` AS S
INNER JOIN (
SELECT DATE(album_date_gmt) AS album_date_gmt, COUNT(*) AS cnt
FROM `%table_albums`
WHERE album_user_id = %user_id
GROUP BY DATE(album_date_gmt)
) AS A ON S.stat_date_gmt = A.album_date_gmt
SET S.stat_albums = GREATEST(S.stat_albums - COALESCE(A.cnt, "0"), 0) WHERE stat_type = "date";
UPDATE IGNORE `%table_stats` SET stat_albums = GREATEST(stat_albums - COALESCE((SELECT COUNT(*) FROM `%table_albums` WHERE album_user_id = %user_id), "0"), 0) WHERE stat_type = "total";';
}
break;
}
break;
}
$sql = strtr($sql_tpl, [
'%table_stats' => $tables['stats'],
'%table_users' => $tables['users'],
'%table_likes' => $tables['likes'],
'%table_albums' => $tables['albums'],
'%related_table' => (isset($args['content_type']) ? ($args['content_type'] . '_') : null) . $args['table'],
'%value' => $value,
'%date_gmt' => $args['date_gmt'],
'%user_id' => $args['user_id'],
'%disk_sum' => $disk_sum_value,
'%likes' => $args['likes'],
]);
try {
DB::queryExec($sql);
} catch (Exception $e) {
throw new StatException($e->getMessage(), 400);
}
}
public static function rebuildTotals()
{
$query = 'TRUNCATE TABLE `%table_prefix%stats`;
INSERT INTO `%table_prefix%stats` (stat_id, stat_date_gmt, stat_type) VALUES ("1", NULL, "total") ON DUPLICATE KEY UPDATE stat_type=stat_type;
UPDATE `%table_prefix%stats` SET
stat_images = (SELECT IFNULL(COUNT(*),0) FROM `%table_prefix%images`),
stat_albums = (SELECT IFNULL(COUNT(*),0) FROM `%table_prefix%albums`),
stat_users = (SELECT IFNULL(COUNT(*),0) FROM `%table_prefix%users`),
stat_image_views = (SELECT IFNULL(SUM(image_views),0) FROM `%table_prefix%images`),
stat_disk_used = (SELECT IFNULL(SUM(image_size) + SUM(image_thumb_size) + SUM(image_medium_size),0) FROM `%table_prefix%images`)
WHERE stat_type = "total";';
$sql = strtr($query, [
'%table_prefix%' => G\get_app_setting('db_table_prefix'),
]);
$db = DB::getInstance();
$db->query($sql);
$db->exec();
}
}
class StatException extends Exception
{
}