feat: add unique listeners analytics
- add unique listener - add some charts - correct minor bugs
This commit is contained in:
parent
9660aa97c8
commit
3a4925816f
|
@ -121,6 +121,14 @@ $routes->group(
|
||||||
'as' => 'podcast-analytics',
|
'as' => 'podcast-analytics',
|
||||||
'filter' => 'permission:podcasts-view,podcast-view',
|
'filter' => 'permission:podcasts-view,podcast-view',
|
||||||
]);
|
]);
|
||||||
|
$routes->get(
|
||||||
|
'analytics-data/(:segment)',
|
||||||
|
'AnalyticsData::getData/$1/$2',
|
||||||
|
[
|
||||||
|
'as' => 'analytics-full-data',
|
||||||
|
'filter' => 'permission:podcasts-view,podcast-view',
|
||||||
|
]
|
||||||
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
'analytics-data/(:segment)/(:segment)',
|
'analytics-data/(:segment)/(:segment)',
|
||||||
'AnalyticsData::getData/$1/$2/$3',
|
'AnalyticsData::getData/$1/$2/$3',
|
||||||
|
|
|
@ -23,14 +23,15 @@ class AnalyticsData extends BaseController
|
||||||
|
|
||||||
public function _remap($method, ...$params)
|
public function _remap($method, ...$params)
|
||||||
{
|
{
|
||||||
if (count($params) > 2) {
|
if (count($params) > 1) {
|
||||||
if (!($this->podcast = (new PodcastModel())->find($params[0]))) {
|
if (!($this->podcast = (new PodcastModel())->find($params[0]))) {
|
||||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(
|
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(
|
||||||
'Podcast not found: ' . $params[0]
|
'Podcast not found: ' . $params[0]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$this->className = '\App\Models\Analytics' . $params[1] . 'Model';
|
$this->className = '\App\Models\Analytics' . $params[1] . 'Model';
|
||||||
$this->methodName = 'getData' . $params[2];
|
$this->methodName =
|
||||||
|
'getData' . (empty($params[2]) ? '' : $params[2]);
|
||||||
if (count($params) > 3) {
|
if (count($params) > 3) {
|
||||||
if (
|
if (
|
||||||
!($this->episode = (new EpisodeModel())
|
!($this->episode = (new EpisodeModel())
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class AddAnalyticsPodcastsByCountry
|
* Class AddAnalyticsPodcasts
|
||||||
* Creates analytics_podcasts_by_country table in database
|
* Creates analytics_podcasts table in database
|
||||||
* @copyright 2020 Podlibre
|
* @copyright 2020 Podlibre
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
* @link https://castopod.org/
|
* @link https://castopod.org/
|
||||||
|
@ -30,6 +30,11 @@ class AddAnalyticsPodcasts extends Migration
|
||||||
'constraint' => 10,
|
'constraint' => 10,
|
||||||
'default' => 1,
|
'default' => 1,
|
||||||
],
|
],
|
||||||
|
'unique_listeners' => [
|
||||||
|
'type' => 'INT',
|
||||||
|
'constraint' => 10,
|
||||||
|
'default' => 1,
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
$this->forge->addPrimaryKey(['podcast_id', 'date']);
|
$this->forge->addPrimaryKey(['podcast_id', 'date']);
|
||||||
$this->forge->addField(
|
$this->forge->addField(
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class AddAnalyticsEpisodesByCountry
|
* Class AddAnalyticsPodcastsByEpisode
|
||||||
* Creates analytics_episodes_by_country table in database
|
* Creates analytics_episodes_by_episode table in database
|
||||||
* @copyright 2020 Podlibre
|
* @copyright 2020 Podlibre
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
* @link https://castopod.org/
|
* @link https://castopod.org/
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class AddAnalyticsWebsiteByReferer
|
* Class AddAnalyticsWebsiteByEntryPage
|
||||||
* Creates analytics_website_by_referer table in database
|
* Creates analytics_website_by_entry_page table in database
|
||||||
* @copyright 2020 Podlibre
|
* @copyright 2020 Podlibre
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
* @link https://castopod.org/
|
* @link https://castopod.org/
|
||||||
|
|
|
@ -17,7 +17,7 @@ class AddAnalyticsPodcastsStoredProcedure extends Migration
|
||||||
public function up()
|
public function up()
|
||||||
{
|
{
|
||||||
// Creates Stored Procedure for data insertion
|
// Creates Stored Procedure for data insertion
|
||||||
// Example: CALL analytics_podcasts(1,2,'FR','phone/android/Deezer');
|
// Example: CALL analytics_podcasts(1, 2, 'FR', 'IDF', 48.853, 2.349, PodcastAddict, 'phone', 'android', 0, 1);
|
||||||
$prefix = $this->db->getPrefix();
|
$prefix = $this->db->getPrefix();
|
||||||
|
|
||||||
$createQuery = <<<EOD
|
$createQuery = <<<EOD
|
||||||
|
@ -31,7 +31,8 @@ CREATE PROCEDURE `{$prefix}analytics_podcasts` (
|
||||||
IN `p_app` VARCHAR(128) CHARSET utf8mb4,
|
IN `p_app` VARCHAR(128) CHARSET utf8mb4,
|
||||||
IN `p_device` VARCHAR(32) CHARSET utf8mb4,
|
IN `p_device` VARCHAR(32) CHARSET utf8mb4,
|
||||||
IN `p_os` VARCHAR(32) CHARSET utf8mb4,
|
IN `p_os` VARCHAR(32) CHARSET utf8mb4,
|
||||||
IN `p_bot` TINYINT(1) UNSIGNED
|
IN `p_bot` TINYINT(1) UNSIGNED,
|
||||||
|
IN `p_new_listener` TINYINT(1) UNSIGNED
|
||||||
) MODIFIES SQL DATA
|
) MODIFIES SQL DATA
|
||||||
DETERMINISTIC
|
DETERMINISTIC
|
||||||
SQL SECURITY INVOKER
|
SQL SECURITY INVOKER
|
||||||
|
@ -40,7 +41,7 @@ BEGIN
|
||||||
IF NOT `p_bot` THEN
|
IF NOT `p_bot` THEN
|
||||||
INSERT INTO `{$prefix}analytics_podcasts`(`podcast_id`, `date`)
|
INSERT INTO `{$prefix}analytics_podcasts`(`podcast_id`, `date`)
|
||||||
VALUES (p_podcast_id, DATE(NOW()))
|
VALUES (p_podcast_id, DATE(NOW()))
|
||||||
ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
|
ON DUPLICATE KEY UPDATE `hits`=`hits`+1, `unique_listeners`=`unique_listeners`+`p_new_listener`;
|
||||||
INSERT INTO `{$prefix}analytics_podcasts_by_episode`(`podcast_id`, `episode_id`, `date`, `age`)
|
INSERT INTO `{$prefix}analytics_podcasts_by_episode`(`podcast_id`, `episode_id`, `date`, `age`)
|
||||||
SELECT p_podcast_id, p_episode_id, DATE(NOW()), datediff(now(),`published_at`) FROM `{$prefix}episodes` WHERE `id`= p_episode_id
|
SELECT p_podcast_id, p_episode_id, DATE(NOW()), datediff(now(),`published_at`) FROM `{$prefix}episodes` WHERE `id`= p_episode_id
|
||||||
ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
|
ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
|
||||||
|
|
|
@ -114,6 +114,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder
|
||||||
'podcast_id' => $podcast->id,
|
'podcast_id' => $podcast->id,
|
||||||
'date' => date('Y-m-d', $date),
|
'date' => date('Y-m-d', $date),
|
||||||
'hits' => $hits,
|
'hits' => $hits,
|
||||||
|
'unique_listeners' => $hits,
|
||||||
];
|
];
|
||||||
$analytics_podcasts_by_country[] = [
|
$analytics_podcasts_by_country[] = [
|
||||||
'podcast_id' => $podcast->id,
|
'podcast_id' => $podcast->id,
|
||||||
|
|
|
@ -18,5 +18,6 @@ class AnalyticsPodcasts extends Entity
|
||||||
'podcast_id' => 'integer',
|
'podcast_id' => 'integer',
|
||||||
'date' => 'datetime',
|
'date' => 'datetime',
|
||||||
'hits' => 'integer',
|
'hits' => 'integer',
|
||||||
|
'unique_listeners' => 'integer',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,7 +199,7 @@ function webpage_hit($podcast_id)
|
||||||
|
|
||||||
$referer = $session->get('referer');
|
$referer = $session->get('referer');
|
||||||
$domain = empty(parse_url($referer, PHP_URL_HOST))
|
$domain = empty(parse_url($referer, PHP_URL_HOST))
|
||||||
? null
|
? '- Direct -'
|
||||||
: parse_url($referer, PHP_URL_HOST);
|
: parse_url($referer, PHP_URL_HOST);
|
||||||
parse_str(parse_url($referer, PHP_URL_QUERY), $queries);
|
parse_str(parse_url($referer, PHP_URL_QUERY), $queries);
|
||||||
$keywords = empty($queries['q']) ? null : $queries['q'];
|
$keywords = empty($queries['q']) ? null : $queries['q'];
|
||||||
|
@ -248,9 +248,13 @@ function podcast_hit($podcastId, $episodeId, $bytesThreshold, $fileSize)
|
||||||
if ($session->get('denyListIp')) {
|
if ($session->get('denyListIp')) {
|
||||||
$session->get('player')['bot'] = true;
|
$session->get('player')['bot'] = true;
|
||||||
}
|
}
|
||||||
$httpRange = $_SERVER['HTTP_RANGE'];
|
//We get the HTTP header field `Range`:
|
||||||
// We create a sha1 hash for this IP_Address+User_Agent+Episode_ID:
|
$httpRange = isset($_SERVER['HTTP_RANGE'])
|
||||||
$hashID =
|
? $_SERVER['HTTP_RANGE']
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// We create a sha1 hash for this IP_Address+User_Agent+Episode_ID (used to count only once multiple episode downloads):
|
||||||
|
$episodeHashId =
|
||||||
'_IpUaEp_' .
|
'_IpUaEp_' .
|
||||||
sha1(
|
sha1(
|
||||||
$_SERVER['REMOTE_ADDR'] .
|
$_SERVER['REMOTE_ADDR'] .
|
||||||
|
@ -260,12 +264,13 @@ function podcast_hit($podcastId, $episodeId, $bytesThreshold, $fileSize)
|
||||||
$episodeId
|
$episodeId
|
||||||
);
|
);
|
||||||
// Was this episode downloaded in the past 24h:
|
// Was this episode downloaded in the past 24h:
|
||||||
$downloadedBytes = cache($hashID);
|
$downloadedBytes = cache($episodeHashId);
|
||||||
// Rolling window is 24 hours (86400 seconds):
|
// Rolling window is 24 hours (86400 seconds):
|
||||||
$ttl = 86400;
|
$rollingTTL = 86400;
|
||||||
if ($downloadedBytes) {
|
if ($downloadedBytes) {
|
||||||
// In case it was already downloaded, TTL should be adjusted (rolling window is 24h since 1st download):
|
// In case it was already downloaded, TTL should be adjusted (rolling window is 24h since 1st download):
|
||||||
$ttl = cache()->getMetadata($hashID)['expire'] - time();
|
$rollingTTL =
|
||||||
|
cache()->getMetadata($episodeHashId)['expire'] - time();
|
||||||
} else {
|
} else {
|
||||||
// If it was never downloaded that means that zero byte were downloaded:
|
// If it was never downloaded that means that zero byte were downloaded:
|
||||||
$downloadedBytes = 0;
|
$downloadedBytes = 0;
|
||||||
|
@ -274,7 +279,7 @@ function podcast_hit($podcastId, $episodeId, $bytesThreshold, $fileSize)
|
||||||
// (Otherwise it means that this was already counted, therefore we don't do anything)
|
// (Otherwise it means that this was already counted, therefore we don't do anything)
|
||||||
if ($downloadedBytes < $bytesThreshold) {
|
if ($downloadedBytes < $bytesThreshold) {
|
||||||
// If HTTP_RANGE is null we are downloading the complete file:
|
// If HTTP_RANGE is null we are downloading the complete file:
|
||||||
if (!isset($httpRange)) {
|
if (!$httpRange) {
|
||||||
$downloadedBytes = $fileSize;
|
$downloadedBytes = $fileSize;
|
||||||
} else {
|
} else {
|
||||||
// [0-1] bytes range requests are used (by Apple) to check that file exists and that 206 partial content is working.
|
// [0-1] bytes range requests are used (by Apple) to check that file exists and that 206 partial content is working.
|
||||||
|
@ -291,19 +296,44 @@ function podcast_hit($podcastId, $episodeId, $bytesThreshold, $fileSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We save the number of downloaded bytes for this user and this episode:
|
// We save the number of downloaded bytes for this user and this episode:
|
||||||
cache()->save($hashID, $downloadedBytes, $ttl);
|
cache()->save($episodeHashId, $downloadedBytes, $rollingTTL);
|
||||||
|
|
||||||
// If more that 1mn was downloaded, we send that to the database:
|
// If more that 1mn was downloaded, that's a hit, we send that to the database:
|
||||||
if ($downloadedBytes >= $bytesThreshold) {
|
if ($downloadedBytes >= $bytesThreshold) {
|
||||||
$db = \Config\Database::connect();
|
$db = \Config\Database::connect();
|
||||||
$procedureName = $db->prefixTable('analytics_podcasts');
|
$procedureName = $db->prefixTable('analytics_podcasts');
|
||||||
|
|
||||||
|
// We create a sha1 hash for this IP_Address+User_Agent+Podcast_ID (used to count unique listeners):
|
||||||
|
$listenerHashId =
|
||||||
|
'_IpUaPo_' .
|
||||||
|
sha1(
|
||||||
|
$_SERVER['REMOTE_ADDR'] .
|
||||||
|
'_' .
|
||||||
|
$_SERVER['HTTP_USER_AGENT'] .
|
||||||
|
'_' .
|
||||||
|
$podcastId
|
||||||
|
);
|
||||||
|
$newListener = 1;
|
||||||
|
// Has this listener already downloaded an episode today:
|
||||||
|
$downloadsByUser = cache($listenerHashId);
|
||||||
|
// We add one download
|
||||||
|
if ($downloadsByUser) {
|
||||||
|
$newListener = 0;
|
||||||
|
$downloadsByUser++;
|
||||||
|
} else {
|
||||||
|
$downloadsByUser = 1;
|
||||||
|
}
|
||||||
|
// Listener count is calculated from 00h00 to 23h59:
|
||||||
|
$midnightTTL = strtotime('tomorrow') - time();
|
||||||
|
// We save the download count for this user until midnight:
|
||||||
|
cache()->save($listenerHashId, $downloadsByUser, $midnightTTL);
|
||||||
|
|
||||||
$app = $session->get('player')['app'];
|
$app = $session->get('player')['app'];
|
||||||
$device = $session->get('player')['device'];
|
$device = $session->get('player')['device'];
|
||||||
$os = $session->get('player')['os'];
|
$os = $session->get('player')['os'];
|
||||||
$bot = $session->get('player')['bot'];
|
$bot = $session->get('player')['bot'];
|
||||||
|
|
||||||
$db->query("CALL $procedureName(?,?,?,?,?,?,?,?,?,?);", [
|
$db->query("CALL $procedureName(?,?,?,?,?,?,?,?,?,?,?);", [
|
||||||
$podcastId,
|
$podcastId,
|
||||||
$episodeId,
|
$episodeId,
|
||||||
$session->get('location')['countryCode'],
|
$session->get('location')['countryCode'],
|
||||||
|
@ -314,10 +344,12 @@ function podcast_hit($podcastId, $episodeId, $bytesThreshold, $fileSize)
|
||||||
$device == null ? '' : $device,
|
$device == null ? '' : $device,
|
||||||
$os == null ? '' : $os,
|
$os == null ? '' : $os,
|
||||||
$bot == null ? 0 : $bot,
|
$bot == null ? 0 : $bot,
|
||||||
|
$newListener,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// If things go wrong the show must go on and the user must be able to download the file
|
// If things go wrong the show must go on and the user must be able to download the file
|
||||||
|
log_message('critical', $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright 2020 Podlibre
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
return [
|
||||||
|
'by_player' => 'Podcast downloads by player (for the past week)',
|
||||||
|
'unique_daily_listeners' => 'Daily unique listeners',
|
||||||
|
'unique_monthly_listeners' => 'Monthly unique listeners',
|
||||||
|
'by_browser' => 'Website usage by browser (for the past week)',
|
||||||
|
'podcast_by_day' => 'Podcast daily downloads',
|
||||||
|
'podcast_by_month' => 'Podcast monthly downloads',
|
||||||
|
'episodes_by_day' =>
|
||||||
|
'5 latest episodes downloads (during their first 60 days)',
|
||||||
|
'by_country' => 'Podcast downloads by country (for the past week)',
|
||||||
|
'by_domain' => 'Website visits by origin (for the past week)',
|
||||||
|
];
|
|
@ -1,26 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class AnalyticsEpisodesByCountry
|
|
||||||
* Model for analytics_episodes_by_country table in database
|
|
||||||
* @copyright 2020 Podlibre
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use CodeIgniter\Model;
|
|
||||||
|
|
||||||
class AnalyticsEpisodesByCountryModel extends Model
|
|
||||||
{
|
|
||||||
protected $table = 'analytics_episodes_by_country';
|
|
||||||
protected $primaryKey = 'id';
|
|
||||||
|
|
||||||
protected $allowedFields = [];
|
|
||||||
|
|
||||||
protected $returnType = \App\Entities\AnalyticsEpisodesByCountry::class;
|
|
||||||
protected $useSoftDeletes = false;
|
|
||||||
|
|
||||||
protected $useTimestamps = false;
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class AnalyticsEpisodesByPlayerModel
|
|
||||||
* Model for analytics_episodes_by_player table in database
|
|
||||||
* @copyright 2020 Podlibre
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use CodeIgniter\Model;
|
|
||||||
|
|
||||||
class AnalyticsEpisodesByPlayerModel extends Model
|
|
||||||
{
|
|
||||||
protected $table = 'analytics_episodes_by_player';
|
|
||||||
protected $primaryKey = 'id';
|
|
||||||
|
|
||||||
protected $allowedFields = [];
|
|
||||||
|
|
||||||
protected $returnType = \App\Entities\AnalyticsEpisodesByPlayer::class;
|
|
||||||
protected $useSoftDeletes = false;
|
|
||||||
|
|
||||||
protected $useTimestamps = false;
|
|
||||||
}
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class AnalyticsPodcastsModel
|
* Class AnalyticsPodcastByCountryModel
|
||||||
* Model for analytics_podcasts table in database
|
* Model for analytics_podcasts_by_country table in database
|
||||||
* @copyright 2020 Podlibre
|
* @copyright 2020 Podlibre
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
* @link https://castopod.org/
|
* @link https://castopod.org/
|
||||||
|
@ -12,44 +12,43 @@ namespace App\Models;
|
||||||
|
|
||||||
use CodeIgniter\Model;
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
class AnalyticsPodcastsModel extends Model
|
class AnalyticsPodcastByCountryModel extends Model
|
||||||
{
|
{
|
||||||
protected $table = 'analytics_podcasts';
|
protected $table = 'analytics_podcasts_by_country';
|
||||||
|
|
||||||
protected $allowedFields = [];
|
protected $allowedFields = [];
|
||||||
|
|
||||||
protected $returnType = \App\Entities\AnalyticsPodcasts::class;
|
protected $returnType = \App\Entities\AnalyticsPodcastsByCountry::class;
|
||||||
protected $useSoftDeletes = false;
|
protected $useSoftDeletes = false;
|
||||||
|
|
||||||
protected $useTimestamps = false;
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all data for a podcast
|
* Gets country data for a podcast
|
||||||
*
|
*
|
||||||
* @param int $podcastId
|
* @param int $podcastId
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getDataByDay(int $podcastId): array
|
public function getData(int $podcastId): array
|
||||||
{
|
{
|
||||||
if (!($found = cache("{$podcastId}_analytics_podcast_by_day"))) {
|
if (!($found = cache("{$podcastId}_analytics_podcast_by_country"))) {
|
||||||
$found = $this->select('`date` as `labels`')
|
$found = $this->select('`country_code` as `labels`')
|
||||||
->selectSum('`hits`', '`values`')
|
->selectSum('`hits`', '`values`')
|
||||||
|
->groupBy('`country_code`')
|
||||||
->where([
|
->where([
|
||||||
'`podcast_id`' => $podcastId,
|
'`podcast_id`' => $podcastId,
|
||||||
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||||
])
|
])
|
||||||
->groupBy('`labels`')
|
->orderBy('`labels`', 'ASC')
|
||||||
->orderBy('`labels``', 'ASC')
|
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
cache()->save(
|
cache()->save(
|
||||||
"{$podcastId}_analytics_podcast_by_day",
|
"{$podcastId}_analytics_podcast_by_country",
|
||||||
$found,
|
$found,
|
||||||
14400
|
600
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $found;
|
return $found;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class AnalyticsPodcastsByEpisodeModel
|
* Class AnalyticsPodcastByEpisodeModel
|
||||||
* Model for analytics_podcasts_by_episodes table in database
|
* Model for analytics_podcasts_by_episodes table in database
|
||||||
* @copyright 2020 Podlibre
|
* @copyright 2020 Podlibre
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
@ -12,7 +12,7 @@ namespace App\Models;
|
||||||
|
|
||||||
use CodeIgniter\Model;
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
class AnalyticsPodcastsByEpisodeModel extends Model
|
class AnalyticsPodcastByEpisodeModel extends Model
|
||||||
{
|
{
|
||||||
protected $table = 'analytics_podcasts_by_episode';
|
protected $table = 'analytics_podcasts_by_episode';
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ class AnalyticsPodcastsByEpisodeModel extends Model
|
||||||
cache()->save(
|
cache()->save(
|
||||||
"{$podcastId}_analytics_podcast_by_episode_by_day",
|
"{$podcastId}_analytics_podcast_by_episode_by_day",
|
||||||
$found,
|
$found,
|
||||||
14400
|
600
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return $found;
|
return $found;
|
||||||
|
@ -104,7 +104,7 @@ class AnalyticsPodcastsByEpisodeModel extends Model
|
||||||
cache()->save(
|
cache()->save(
|
||||||
"{$podcastId}_{$episodeId}_analytics_podcast_by_episode_by_day",
|
"{$podcastId}_{$episodeId}_analytics_podcast_by_episode_by_day",
|
||||||
$found,
|
$found,
|
||||||
14400
|
600
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return $found;
|
return $found;
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class AnalyticsPodcastsByPlayerModel
|
* Class AnalyticsPodcastByPlayerModel
|
||||||
* Model for analytics_podcasts_by_player table in database
|
* Model for analytics_podcasts_by_player table in database
|
||||||
* @copyright 2020 Podlibre
|
* @copyright 2020 Podlibre
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
@ -12,7 +12,7 @@ namespace App\Models;
|
||||||
|
|
||||||
use CodeIgniter\Model;
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
class AnalyticsPodcastsByPlayerModel extends Model
|
class AnalyticsPodcastByPlayerModel extends Model
|
||||||
{
|
{
|
||||||
protected $table = 'analytics_podcasts_by_player';
|
protected $table = 'analytics_podcasts_by_player';
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ class AnalyticsPodcastsByPlayerModel extends Model
|
||||||
protected $useTimestamps = false;
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all data for a podcast
|
* Gets player data for a podcast
|
||||||
*
|
*
|
||||||
* @param int $podcastId
|
* @param int $podcastId
|
||||||
*
|
*
|
||||||
|
@ -41,18 +41,18 @@ class AnalyticsPodcastsByPlayerModel extends Model
|
||||||
->selectSum('`hits`', '`values`')
|
->selectSum('`hits`', '`values`')
|
||||||
->where([
|
->where([
|
||||||
'`podcast_id`' => $podcastId,
|
'`podcast_id`' => $podcastId,
|
||||||
'`app` !=' => null,
|
'`app` !=' => '',
|
||||||
'`bot`' => 0,
|
'`bot`' => 0,
|
||||||
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||||
])
|
])
|
||||||
->groupBy('`labels`')
|
->groupBy('`labels`')
|
||||||
->orderBy('`values``', 'DESC')
|
->orderBy('`values`', 'DESC')
|
||||||
->findAll(10);
|
->findAll(10);
|
||||||
|
|
||||||
cache()->save(
|
cache()->save(
|
||||||
"{$podcastId}_analytics_podcasts_by_player_by_app",
|
"{$podcastId}_analytics_podcasts_by_player_by_app",
|
||||||
$found,
|
$found,
|
||||||
14400
|
600
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class AnalyticsPodcastsByPlayerModel extends Model
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all data for a podcast
|
* Gets device data for a podcast
|
||||||
*
|
*
|
||||||
* @param int $podcastId
|
* @param int $podcastId
|
||||||
*
|
*
|
||||||
|
@ -84,7 +84,7 @@ class AnalyticsPodcastsByPlayerModel extends Model
|
||||||
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||||
])
|
])
|
||||||
->groupBy('`ids`')
|
->groupBy('`ids`')
|
||||||
->orderBy('`values``', 'DESC')
|
->orderBy('`values`', 'DESC')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
$foundOs = $this->select(
|
$foundOs = $this->select(
|
||||||
|
@ -98,7 +98,7 @@ class AnalyticsPodcastsByPlayerModel extends Model
|
||||||
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||||
])
|
])
|
||||||
->groupBy('`ids`')
|
->groupBy('`ids`')
|
||||||
->orderBy('`values``', 'DESC')
|
->orderBy('`values`', 'DESC')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
$foundDevice = $this->select(
|
$foundDevice = $this->select(
|
||||||
|
@ -112,7 +112,7 @@ class AnalyticsPodcastsByPlayerModel extends Model
|
||||||
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||||
])
|
])
|
||||||
->groupBy('`ids`')
|
->groupBy('`ids`')
|
||||||
->orderBy('`values``', 'DESC')
|
->orderBy('`values`', 'DESC')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
$foundBot = $this->select(
|
$foundBot = $this->select(
|
||||||
|
@ -125,14 +125,14 @@ class AnalyticsPodcastsByPlayerModel extends Model
|
||||||
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||||
])
|
])
|
||||||
->groupBy('`ids`')
|
->groupBy('`ids`')
|
||||||
->orderBy('`values``', 'DESC')
|
->orderBy('`values`', 'DESC')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
$found = array_merge($foundApp, $foundOs, $foundDevice, $foundBot);
|
$found = array_merge($foundApp, $foundOs, $foundDevice, $foundBot);
|
||||||
cache()->save(
|
cache()->save(
|
||||||
"{$podcastId}_analytics_podcasts_by_player_by_device",
|
"{$podcastId}_analytics_podcasts_by_player_by_device",
|
||||||
$found,
|
$found,
|
||||||
14400
|
600
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AnalyticsPodcastByRegionModel
|
||||||
|
* Model for analytics_podcasts_by_region table in database
|
||||||
|
* @copyright 2020 Podlibre
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class AnalyticsPodcastByRegionModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'analytics_podcasts_by_region';
|
||||||
|
|
||||||
|
protected $allowedFields = [];
|
||||||
|
|
||||||
|
protected $returnType = \App\Entities\AnalyticsPodcastsByRegion::class;
|
||||||
|
protected $useSoftDeletes = false;
|
||||||
|
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets region data for a podcast
|
||||||
|
*
|
||||||
|
* @param int $podcastId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getData(int $podcastId): array
|
||||||
|
{
|
||||||
|
if (!($found = cache("{$podcastId}_analytics_podcast_by_region"))) {
|
||||||
|
$found = $this->select(
|
||||||
|
'`country_code`, `region_code`, `latitude`, `longitude`'
|
||||||
|
)
|
||||||
|
->selectSum('`hits`', '`values`')
|
||||||
|
->groupBy(
|
||||||
|
'`country_code`, `region_code`, `latitude`, `longitude`'
|
||||||
|
)
|
||||||
|
->where([
|
||||||
|
'`podcast_id`' => $podcastId,
|
||||||
|
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||||
|
])
|
||||||
|
->orderBy('`country_code`, `region_code`', 'ASC')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
cache()->save(
|
||||||
|
"{$podcastId}_analytics_podcast_by_region",
|
||||||
|
$found,
|
||||||
|
600
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AnalyticsPodcastModel
|
||||||
|
* Model for analytics_podcasts table in database
|
||||||
|
* @copyright 2020 Podlibre
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class AnalyticsPodcastModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'analytics_podcasts';
|
||||||
|
|
||||||
|
protected $allowedFields = [];
|
||||||
|
|
||||||
|
protected $returnType = \App\Entities\AnalyticsPodcasts::class;
|
||||||
|
protected $useSoftDeletes = false;
|
||||||
|
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets hits data for a podcast
|
||||||
|
*
|
||||||
|
* @param int $podcastId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getDataByDay(int $podcastId): array
|
||||||
|
{
|
||||||
|
if (!($found = cache("{$podcastId}_analytics_podcast_by_day"))) {
|
||||||
|
$found = $this->select('`date` as `labels`, `hits` as `values`')
|
||||||
|
->where([
|
||||||
|
'`podcast_id`' => $podcastId,
|
||||||
|
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
||||||
|
])
|
||||||
|
->orderBy('`labels`', 'ASC')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
cache()->save("{$podcastId}_analytics_podcast_by_day", $found, 600);
|
||||||
|
}
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets hits data for a podcast
|
||||||
|
*
|
||||||
|
* @param int $podcastId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getDataByMonth(int $podcastId): array
|
||||||
|
{
|
||||||
|
if (!($found = cache("{$podcastId}_analytics_podcast_by_month"))) {
|
||||||
|
$found = $this->select(
|
||||||
|
'concat(year(`date`),"-",month(`date`),"-01") as `labels`'
|
||||||
|
)
|
||||||
|
->selectSum('`hits`', '`values`')
|
||||||
|
->where([
|
||||||
|
'`podcast_id`' => $podcastId,
|
||||||
|
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
||||||
|
])
|
||||||
|
->orderBy('`date`', 'ASC')
|
||||||
|
->groupBy('concat(month(`date`),"-",year(`date`))')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
cache()->save(
|
||||||
|
"{$podcastId}_analytics_podcast_by_month",
|
||||||
|
$found,
|
||||||
|
600
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets unique listeners data for a podcast
|
||||||
|
*
|
||||||
|
* @param int $podcastId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getDataUniqueListenersByDay(int $podcastId): array
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!($found = cache(
|
||||||
|
"{$podcastId}_analytics_podcast_unique_listeners_by_day"
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
$found = $this->select(
|
||||||
|
'`date` as `labels`, `unique_listeners` as `values`'
|
||||||
|
)
|
||||||
|
->where([
|
||||||
|
'`podcast_id`' => $podcastId,
|
||||||
|
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
||||||
|
])
|
||||||
|
->orderBy('`labels`', 'ASC')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
cache()->save(
|
||||||
|
"{$podcastId}_analytics_podcast_unique_listeners_by_day",
|
||||||
|
$found,
|
||||||
|
600
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets unique listeners data for a podcast
|
||||||
|
*
|
||||||
|
* @param int $podcastId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getDataUniqueListenersByMonth(int $podcastId): array
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!($found = cache(
|
||||||
|
"{$podcastId}_analytics_podcast_unique_listeners_by_month"
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
$found = $this->select(
|
||||||
|
'concat(year(`date`),"-",month(`date`),"-01") as `labels`'
|
||||||
|
)
|
||||||
|
->selectSum('`unique_listeners`', '`values`')
|
||||||
|
->where([
|
||||||
|
'`podcast_id`' => $podcastId,
|
||||||
|
])
|
||||||
|
->groupBy('concat(month(`date`),"-",year(`date`))')
|
||||||
|
->orderBy('`date`', 'ASC')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
cache()->save(
|
||||||
|
"{$podcastId}_analytics_podcast_unique_listeners_by_month",
|
||||||
|
$found,
|
||||||
|
600
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class AnalyticsPodcastsByCountryModel
|
|
||||||
* Model for analytics_podcasts_by_country table in database
|
|
||||||
* @copyright 2020 Podlibre
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use CodeIgniter\Model;
|
|
||||||
|
|
||||||
class AnalyticsPodcastsByCountryModel extends Model
|
|
||||||
{
|
|
||||||
protected $table = 'analytics_podcasts_by_country';
|
|
||||||
|
|
||||||
protected $allowedFields = [];
|
|
||||||
|
|
||||||
protected $returnType = \App\Entities\AnalyticsPodcastsByCountry::class;
|
|
||||||
protected $useSoftDeletes = false;
|
|
||||||
|
|
||||||
protected $useTimestamps = false;
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class AnalyticsPodcastsByRegionModel
|
|
||||||
* Model for analytics_podcasts_by_region table in database
|
|
||||||
* @copyright 2020 Podlibre
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use CodeIgniter\Model;
|
|
||||||
|
|
||||||
class AnalyticsPodcastsByRegionModel extends Model
|
|
||||||
{
|
|
||||||
protected $table = 'analytics_podcasts_by_region';
|
|
||||||
|
|
||||||
protected $allowedFields = [];
|
|
||||||
|
|
||||||
protected $returnType = \App\Entities\AnalyticsPodcastsByRegion::class;
|
|
||||||
protected $useSoftDeletes = false;
|
|
||||||
|
|
||||||
protected $useTimestamps = false;
|
|
||||||
}
|
|
|
@ -22,4 +22,33 @@ class AnalyticsWebsiteByBrowserModel extends Model
|
||||||
protected $useSoftDeletes = false;
|
protected $useSoftDeletes = false;
|
||||||
|
|
||||||
protected $useTimestamps = false;
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets browser data for a podcast
|
||||||
|
*
|
||||||
|
* @param int $podcastId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getData(int $podcastId): array
|
||||||
|
{
|
||||||
|
if (!($found = cache("{$podcastId}_analytics_website_by_browser"))) {
|
||||||
|
$found = $this->select('`browser` as `labels`')
|
||||||
|
->selectSum('`hits`', '`values`')
|
||||||
|
->groupBy('`browser`')
|
||||||
|
->where([
|
||||||
|
'`podcast_id`' => $podcastId,
|
||||||
|
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||||
|
])
|
||||||
|
->orderBy('`browser`', 'ASC')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
cache()->save(
|
||||||
|
"{$podcastId}_analytics_website_by_browser",
|
||||||
|
$found,
|
||||||
|
600
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class AnalyticsWebsiteByCountryModel
|
|
||||||
* Model for analytics_website_by_country table in database
|
|
||||||
* @copyright 2020 Podlibre
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use CodeIgniter\Model;
|
|
||||||
|
|
||||||
class AnalyticsWebsiteByCountryModel extends Model
|
|
||||||
{
|
|
||||||
protected $table = 'analytics_website_by_country';
|
|
||||||
protected $primaryKey = 'id';
|
|
||||||
|
|
||||||
protected $allowedFields = [];
|
|
||||||
|
|
||||||
protected $returnType = \App\Entities\AnalyticsWebsiteByCountry::class;
|
|
||||||
protected $useSoftDeletes = false;
|
|
||||||
|
|
||||||
protected $useTimestamps = false;
|
|
||||||
}
|
|
|
@ -22,4 +22,33 @@ class AnalyticsWebsiteByEntryPageModel extends Model
|
||||||
protected $useSoftDeletes = false;
|
protected $useSoftDeletes = false;
|
||||||
|
|
||||||
protected $useTimestamps = false;
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets entry pages data for a podcast
|
||||||
|
*
|
||||||
|
* @param int $podcastId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getData(int $podcastId): array
|
||||||
|
{
|
||||||
|
if (!($found = cache("{$podcastId}_analytics_website_by_entry_page"))) {
|
||||||
|
$found = $this->select('`entry_page` as `labels`')
|
||||||
|
->selectSum('`hits`', '`values`')
|
||||||
|
->groupBy('`entry_page`')
|
||||||
|
->where([
|
||||||
|
'`podcast_id`' => $podcastId,
|
||||||
|
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||||
|
])
|
||||||
|
->orderBy('`entry_page`', 'ASC')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
cache()->save(
|
||||||
|
"{$podcastId}_analytics_website_by_entry_page",
|
||||||
|
$found,
|
||||||
|
600
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,4 +22,62 @@ class AnalyticsWebsiteByRefererModel extends Model
|
||||||
protected $useSoftDeletes = false;
|
protected $useSoftDeletes = false;
|
||||||
|
|
||||||
protected $useTimestamps = false;
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets referer data for a podcast
|
||||||
|
*
|
||||||
|
* @param int $podcastId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getData(int $podcastId): array
|
||||||
|
{
|
||||||
|
if (!($found = cache("{$podcastId}_analytics_website_by_referer"))) {
|
||||||
|
$found = $this->select('`referer` as `labels`')
|
||||||
|
->selectSum('`hits`', '`values`')
|
||||||
|
->groupBy('`referer`')
|
||||||
|
->where([
|
||||||
|
'`podcast_id`' => $podcastId,
|
||||||
|
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||||
|
])
|
||||||
|
->orderBy('`referer`', 'ASC')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
cache()->save(
|
||||||
|
"{$podcastId}_analytics_website_by_referer",
|
||||||
|
$found,
|
||||||
|
600
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets domain data for a podcast
|
||||||
|
*
|
||||||
|
* @param int $podcastId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getDataByDomain(int $podcastId): array
|
||||||
|
{
|
||||||
|
if (!($found = cache("{$podcastId}_analytics_website_by_domain"))) {
|
||||||
|
$found = $this->select('`domain` as `labels`')
|
||||||
|
->selectSum('`hits`', '`values`')
|
||||||
|
->groupBy('`domain`')
|
||||||
|
->where([
|
||||||
|
'`podcast_id`' => $podcastId,
|
||||||
|
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||||
|
])
|
||||||
|
->orderBy('`domain`', 'ASC')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
cache()->save(
|
||||||
|
"{$podcastId}_analytics_website_by_domain",
|
||||||
|
$found,
|
||||||
|
600
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,24 +9,76 @@
|
||||||
<?= $this->endSection() ?>
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
<?= $this->section('content') ?>
|
<?= $this->section('content') ?>
|
||||||
<div class="h-64" id="by-app-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
|
||||||
'analytics-data',
|
<h2><?= lang('Charts.podcast_by_day') ?></h2>
|
||||||
$podcast->id,
|
|
||||||
'PodcastsByPlayer',
|
|
||||||
'ByApp'
|
|
||||||
) ?>"></div>
|
|
||||||
<div class="h-64" id="by-day-graph" data-chart-type="xy-chart" data-chart-url="<?= route_to(
|
<div class="h-64" id="by-day-graph" data-chart-type="xy-chart" data-chart-url="<?= route_to(
|
||||||
'analytics-data',
|
'analytics-data',
|
||||||
$podcast->id,
|
$podcast->id,
|
||||||
'Podcasts',
|
'Podcast',
|
||||||
'ByDay'
|
'ByDay'
|
||||||
) ?>"></div>
|
) ?>"></div>
|
||||||
|
|
||||||
|
<h2><?= lang('Charts.podcast_by_month') ?></h2>
|
||||||
|
<div class="h-64" id="by-month-graph" data-chart-type="xy-chart" data-chart-url="<?= route_to(
|
||||||
|
'analytics-data',
|
||||||
|
$podcast->id,
|
||||||
|
'Podcast',
|
||||||
|
'ByMonth'
|
||||||
|
) ?>"></div>
|
||||||
|
|
||||||
|
<h2><?= lang('Charts.unique_daily_listeners') ?></h2>
|
||||||
|
<div class="h-64" id="by-day-listeners-graph" data-chart-type="xy-chart" data-chart-url="<?= route_to(
|
||||||
|
'analytics-data',
|
||||||
|
$podcast->id,
|
||||||
|
'Podcast',
|
||||||
|
'UniqueListenersByDay'
|
||||||
|
) ?>"></div>
|
||||||
|
|
||||||
|
<h2><?= lang('Charts.unique_monthly_listeners') ?></h2>
|
||||||
|
<div class="h-64" id="by-month-listeners-graph" data-chart-type="xy-chart" data-chart-url="<?= route_to(
|
||||||
|
'analytics-data',
|
||||||
|
$podcast->id,
|
||||||
|
'Podcast',
|
||||||
|
'UniqueListenersByMonth'
|
||||||
|
) ?>"></div>
|
||||||
|
|
||||||
|
<h2><?= lang('Charts.episodes_by_day') ?></h2>
|
||||||
<div class="h-64" id="by-age-graph" data-chart-type="xy-series-chart" data-chart-url="<?= route_to(
|
<div class="h-64" id="by-age-graph" data-chart-type="xy-series-chart" data-chart-url="<?= route_to(
|
||||||
'analytics-data',
|
'analytics-data',
|
||||||
$podcast->id,
|
$podcast->id,
|
||||||
'PodcastsByEpisode',
|
'PodcastByEpisode',
|
||||||
'ByDay'
|
'ByDay'
|
||||||
) ?>"></div>
|
) ?>"></div>
|
||||||
|
|
||||||
|
<h2><?= lang('Charts.by_player') ?></h2>
|
||||||
|
<div class="h-64" id="by-app-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||||
|
'analytics-data',
|
||||||
|
$podcast->id,
|
||||||
|
'PodcastByPlayer',
|
||||||
|
'ByApp'
|
||||||
|
) ?>"></div>
|
||||||
|
|
||||||
|
<h2><?= lang('Charts.by_browser') ?></h2>
|
||||||
|
<div class="h-64" id="by-browser-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||||
|
'analytics-full-data',
|
||||||
|
$podcast->id,
|
||||||
|
'WebsiteByBrowser'
|
||||||
|
) ?>"></div>
|
||||||
|
|
||||||
|
<h2><?= lang('Charts.by_country') ?></h2>
|
||||||
|
<div class="h-64" id="by-country-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||||
|
'analytics-full-data',
|
||||||
|
$podcast->id,
|
||||||
|
'PodcastByCountry'
|
||||||
|
) ?>"></div>
|
||||||
|
|
||||||
|
<h2><?= lang('Charts.by_domain') ?></h2>
|
||||||
|
<div class="h-64" id="by-domain-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||||
|
'analytics-data',
|
||||||
|
$podcast->id,
|
||||||
|
'WebsiteByReferer',
|
||||||
|
'ByDomain'
|
||||||
|
) ?>"></div>
|
||||||
|
|
||||||
<script src="/assets/charts.js" type="module"></script>
|
<script src="/assets/charts.js" type="module"></script>
|
||||||
<?= $this->endSection() ?>
|
<?= $this->endSection() ?>
|
||||||
|
|
Loading…
Reference in New Issue