mirror of
https://code.castopod.org/adaures/castopod
synced 2025-04-19 13:01:19 +00:00
feat: add map analytics, add episodes analytics, clean analytics page layout, translate countries
This commit is contained in:
parent
196920d62f
commit
07eae83a00
@ -123,10 +123,46 @@ $routes->group(
|
||||
'as' => 'podcast-delete',
|
||||
'filter' => 'permission:podcasts-delete',
|
||||
]);
|
||||
$routes->get('analytics', 'Podcast::analytics/$1', [
|
||||
'as' => 'podcast-analytics',
|
||||
'filter' => 'permission:podcasts-view,podcast-view',
|
||||
]);
|
||||
|
||||
$routes->group('analytics', function ($routes) {
|
||||
$routes->get('/', 'Podcast::viewAnalytics/$1', [
|
||||
'as' => 'podcast-analytics',
|
||||
'filter' => 'permission:podcasts-view,podcast-view',
|
||||
]);
|
||||
$routes->get(
|
||||
'webpages',
|
||||
'Podcast::viewAnalyticsWebpages/$1',
|
||||
[
|
||||
'as' => 'podcast-analytics-webpages',
|
||||
'filter' => 'permission:podcasts-view,podcast-view',
|
||||
]
|
||||
);
|
||||
$routes->get(
|
||||
'locations',
|
||||
'Podcast::viewAnalyticsLocations/$1',
|
||||
[
|
||||
'as' => 'podcast-analytics-locations',
|
||||
'filter' => 'permission:podcasts-view,podcast-view',
|
||||
]
|
||||
);
|
||||
$routes->get(
|
||||
'unique-listeners',
|
||||
'Podcast::viewAnalyticsUniqueListeners/$1',
|
||||
[
|
||||
'as' => 'podcast-analytics-unique-listeners',
|
||||
'filter' => 'permission:podcasts-view,podcast-view',
|
||||
]
|
||||
);
|
||||
$routes->get(
|
||||
'players',
|
||||
'Podcast::viewAnalyticsPlayers/$1',
|
||||
[
|
||||
'as' => 'podcast-analytics-players',
|
||||
'filter' => 'permission:podcasts-view,podcast-view',
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
$routes->get(
|
||||
'analytics-data/(:segment)',
|
||||
'AnalyticsData::getData/$1/$2',
|
||||
|
@ -58,12 +58,44 @@ class Podcast extends BaseController
|
||||
return view('admin/podcast/view', $data);
|
||||
}
|
||||
|
||||
public function analytics()
|
||||
public function viewAnalytics()
|
||||
{
|
||||
$data = ['podcast' => $this->podcast];
|
||||
|
||||
replace_breadcrumb_params([0 => $this->podcast->title]);
|
||||
return view('admin/podcast/analytics', $data);
|
||||
return view('admin/podcast/analytics/index', $data);
|
||||
}
|
||||
|
||||
public function viewAnalyticsWebpages()
|
||||
{
|
||||
$data = ['podcast' => $this->podcast];
|
||||
|
||||
replace_breadcrumb_params([0 => $this->podcast->title]);
|
||||
return view('admin/podcast/analytics/webpages', $data);
|
||||
}
|
||||
|
||||
public function viewAnalyticsLocations()
|
||||
{
|
||||
$data = ['podcast' => $this->podcast];
|
||||
|
||||
replace_breadcrumb_params([0 => $this->podcast->title]);
|
||||
return view('admin/podcast/analytics/locations', $data);
|
||||
}
|
||||
|
||||
public function viewAnalyticsUniqueListeners()
|
||||
{
|
||||
$data = ['podcast' => $this->podcast];
|
||||
|
||||
replace_breadcrumb_params([0 => $this->podcast->title]);
|
||||
return view('admin/podcast/analytics/unique_listeners', $data);
|
||||
}
|
||||
|
||||
public function viewAnalyticsPlayers()
|
||||
{
|
||||
$data = ['podcast' => $this->podcast];
|
||||
|
||||
replace_breadcrumb_params([0 => $this->podcast->title]);
|
||||
return view('admin/podcast/analytics/players', $data);
|
||||
}
|
||||
|
||||
public function create()
|
||||
|
@ -20,4 +20,9 @@ class AnalyticsPodcastsByCountry extends Entity
|
||||
'date' => 'datetime',
|
||||
'hits' => 'integer',
|
||||
];
|
||||
|
||||
public function getLabels()
|
||||
{
|
||||
return lang('Countries.' . $this->attributes['labels']);
|
||||
}
|
||||
}
|
||||
|
@ -23,4 +23,9 @@ class AnalyticsPodcastsByRegion extends Entity
|
||||
'date' => 'datetime',
|
||||
'hits' => 'integer',
|
||||
];
|
||||
|
||||
public function getCountryCode()
|
||||
{
|
||||
return lang('Countries.' . $this->attributes['country_code']);
|
||||
}
|
||||
}
|
||||
|
@ -20,4 +20,10 @@ class AnalyticsWebsiteByEntryPage extends Entity
|
||||
'date' => 'datetime',
|
||||
'hits' => 'integer',
|
||||
];
|
||||
|
||||
public function getLabels()
|
||||
{
|
||||
$split = explode('/', $this->attributes['labels']);
|
||||
return $split[count($split) - 1];
|
||||
}
|
||||
}
|
||||
|
@ -23,4 +23,8 @@ return [
|
||||
'settings' => 'settings',
|
||||
'platforms' => 'platforms',
|
||||
'analytics' => 'Analytics',
|
||||
'locations' => 'Locations',
|
||||
'website' => 'Website',
|
||||
'unique-listeners' => 'Unique listeners',
|
||||
'players' => 'Players',
|
||||
];
|
||||
|
@ -7,14 +7,24 @@
|
||||
*/
|
||||
|
||||
return [
|
||||
'by_player' => 'Podcast downloads by player (for the past week)',
|
||||
'by_player_weekly' => 'Podcast downloads by player (for the past week)',
|
||||
'by_player_yearly' => 'Podcast downloads by player (for the past year)',
|
||||
'by_device_weekly' => 'Podcast downloads by device (for the past week)',
|
||||
'by_os_weekly' => 'Podcast downloads by O.S. (for the past week)',
|
||||
'podcast_by_region' => 'Podcast downloads by region (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)',
|
||||
'by_browser' => 'Web pages usage by browser (for the past week)',
|
||||
'podcast_by_day' => 'Podcast daily downloads',
|
||||
'podcast_by_month' => 'Podcast monthly downloads',
|
||||
'episode_by_day' => 'Episode daily downloads (first 60 days)',
|
||||
'episode_by_month' => 'Episode 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)',
|
||||
'by_country_weekly' => 'Podcast downloads by country (for the past week)',
|
||||
'by_country_yearly' => 'Podcast downloads by country (for the past year)',
|
||||
'by_domain_weekly' => 'Web pages visits by source (for the past week)',
|
||||
'by_domain_yearly' => 'Web pages visits by source (for the past year)',
|
||||
'by_entry_page' => 'Web pages visits by landing page (for the past week)',
|
||||
'podcast_bots' => 'Bots (crawlers)',
|
||||
];
|
||||
|
@ -20,5 +20,9 @@ return [
|
||||
'contributor-add' => 'Add contributor',
|
||||
'settings' => 'Settings',
|
||||
'platforms' => 'Podcast platforms',
|
||||
'podcast-analytics' => 'Audiences Overview',
|
||||
'podcast-analytics' => 'Audience overview',
|
||||
'podcast-analytics-webpages' => 'Web pages visits',
|
||||
'podcast-analytics-locations' => 'Locations',
|
||||
'podcast-analytics-unique-listeners' => 'Unique listeners',
|
||||
'podcast-analytics-players' => 'Players',
|
||||
];
|
||||
|
@ -20,7 +20,7 @@ return [
|
||||
'unique_daily_listeners' => 'Auditeurs uniques quotidiens',
|
||||
'unique_monthly_listeners' => 'Auditeurs uniques mensuels',
|
||||
'by_browser' =>
|
||||
'Fréquentation du site par navigateur (sur la dernière semaine)',
|
||||
'Fréquentation des pages web par navigateur (sur la dernière semaine)',
|
||||
'podcast_by_day' => 'Téléchargements quotidiens de podcasts',
|
||||
'podcast_by_month' => 'Téléchargements mensuels de podcasts',
|
||||
'episode_by_day' =>
|
||||
@ -33,10 +33,10 @@ return [
|
||||
'by_country_yearly' =>
|
||||
'Téléchargement de podcasts par pays (sur la dernière année)',
|
||||
'by_domain_weekly' =>
|
||||
'Fréquentation du site par origine (sur la dernière semaine)',
|
||||
'Fréquentation des pages web par origine (sur la dernière semaine)',
|
||||
'by_domain_yearly' =>
|
||||
'Fréquentation du site par origine (sur la dernière année)',
|
||||
'Fréquentation des pages web par origine (sur la dernière année)',
|
||||
'by_entry_page' =>
|
||||
'Fréquentation du site par page d’entrée (sur la dernière semaine)',
|
||||
'Fréquentation des pages web par page d’entrée (sur la dernière semaine)',
|
||||
'podcast_bots' => 'Robots (bots)',
|
||||
];
|
||||
|
@ -20,8 +20,8 @@ return [
|
||||
'contributor-add' => 'Ajouter un contributeur',
|
||||
'settings' => 'Paramètres',
|
||||
'platforms' => 'Plateformes du podcast',
|
||||
'podcast-analytics' => 'Mesures d’audience',
|
||||
'podcast-analytics-website' => 'Visites du site web',
|
||||
'podcast-analytics' => 'Vue d’ensemble',
|
||||
'podcast-analytics-webpages' => 'Visites des pages web',
|
||||
'podcast-analytics-locations' => 'Localisations',
|
||||
'podcast-analytics-unique-listeners' => 'Auditeurs uniques',
|
||||
'podcast-analytics-players' => 'Lecteurs',
|
||||
|
@ -30,9 +30,14 @@ class AnalyticsPodcastByCountryModel extends Model
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getData(int $podcastId): array
|
||||
public function getDataWeekly(int $podcastId): array
|
||||
{
|
||||
if (!($found = cache("{$podcastId}_analytics_podcast_by_country"))) {
|
||||
$locale = service('request')->getLocale();
|
||||
if (
|
||||
!($found = cache(
|
||||
"{$podcastId}_analytics_podcast_by_country_weekly_{$locale}"
|
||||
))
|
||||
) {
|
||||
$found = $this->select('`country_code` as `labels`')
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
@ -44,7 +49,41 @@ class AnalyticsPodcastByCountryModel extends Model
|
||||
->findAll(10);
|
||||
|
||||
cache()->save(
|
||||
"{$podcastId}_analytics_podcast_by_country",
|
||||
"{$podcastId}_analytics_podcast_by_country_weekly_{$locale}",
|
||||
$found,
|
||||
600
|
||||
);
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets country data for a podcast
|
||||
*
|
||||
* @param int $podcastId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDataYearly(int $podcastId): array
|
||||
{
|
||||
$locale = service('request')->getLocale();
|
||||
if (
|
||||
!($found = cache(
|
||||
"{$podcastId}_analytics_podcast_by_country_yearly_{$locale}"
|
||||
))
|
||||
) {
|
||||
$found = $this->select('`country_code` as `labels`')
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
||||
])
|
||||
->groupBy('`labels`')
|
||||
->orderBy('`values`', 'DESC')
|
||||
->findAll(10);
|
||||
|
||||
cache()->save(
|
||||
"{$podcastId}_analytics_podcast_by_country_yearly_{$locale}",
|
||||
$found,
|
||||
600
|
||||
);
|
||||
|
@ -37,12 +37,12 @@ class AnalyticsPodcastByEpisodeModel extends Model
|
||||
))
|
||||
) {
|
||||
$lastEpisodes = (new EpisodeModel())
|
||||
->select('id, season_number, number, title')
|
||||
->orderBy('id', 'DESC')
|
||||
->where(['podcast_id' => $podcastId])
|
||||
->select('`id`, `season_number`, `number`, `title`')
|
||||
->orderBy('`id`', 'DESC')
|
||||
->where(['`podcast_id`' => $podcastId])
|
||||
->findAll(5);
|
||||
|
||||
$found = $this->select('age AS X');
|
||||
$found = $this->select('`age` AS `X`');
|
||||
|
||||
$letter = 97;
|
||||
foreach ($lastEpisodes as $episode) {
|
||||
@ -51,7 +51,7 @@ class AnalyticsPodcastByEpisodeModel extends Model
|
||||
'(CASE WHEN `episode_id`=' .
|
||||
$episode->id .
|
||||
' THEN `hits` END)',
|
||||
chr($letter) . 'Y'
|
||||
'`' . chr($letter) . 'Y`'
|
||||
)
|
||||
->select(
|
||||
'"' .
|
||||
@ -62,20 +62,20 @@ class AnalyticsPodcastByEpisodeModel extends Model
|
||||
? ''
|
||||
: '-' . $episode->number . '/ ') .
|
||||
$episode->title .
|
||||
'" AS ' .
|
||||
'" AS `' .
|
||||
chr($letter) .
|
||||
'Value'
|
||||
'Value`'
|
||||
);
|
||||
$letter++;
|
||||
}
|
||||
|
||||
$found = $found
|
||||
->where([
|
||||
'podcast_id' => $podcastId,
|
||||
'age <' => 60,
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`age` <' => 60,
|
||||
])
|
||||
->groupBy('X')
|
||||
->orderBy('X', 'ASC')
|
||||
->groupBy('`X`')
|
||||
->orderBy('`X`', 'ASC')
|
||||
->findAll();
|
||||
|
||||
cache()->save(
|
||||
@ -91,14 +91,15 @@ class AnalyticsPodcastByEpisodeModel extends Model
|
||||
"{$podcastId}_{$episodeId}_analytics_podcast_by_episode_by_day"
|
||||
))
|
||||
) {
|
||||
$found = $this->select('date as labels')
|
||||
->selectSum('hits', 'values')
|
||||
$found = $this->select('`date as `labels`')
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
'episode_id' => $episodeId,
|
||||
'podcast_id' => $podcastId,
|
||||
'`episode_id`' => $episodeId,
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`age` <' => 60,
|
||||
])
|
||||
->groupBy('labels')
|
||||
->orderBy('labels', 'ASC')
|
||||
->groupBy('`labels`')
|
||||
->orderBy('`labels`', 'ASC')
|
||||
->findAll();
|
||||
|
||||
cache()->save(
|
||||
@ -110,4 +111,35 @@ class AnalyticsPodcastByEpisodeModel extends Model
|
||||
return $found;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $podcastId, $episodeId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDataByMonth(int $podcastId, int $episodeId = null): array
|
||||
{
|
||||
if (
|
||||
!($found = cache(
|
||||
"{$podcastId}_{$episodeId}_analytics_podcast_by_episode_by_month"
|
||||
))
|
||||
) {
|
||||
$found = $this->select('DATE_FORMAT(`date`,"%Y-%m-01") as `labels`')
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
'episode_id' => $episodeId,
|
||||
'podcast_id' => $podcastId,
|
||||
])
|
||||
->groupBy('`labels`')
|
||||
->orderBy('`labels`', 'ASC')
|
||||
->findAll();
|
||||
|
||||
cache()->save(
|
||||
"{$podcastId}_{$episodeId}_analytics_podcast_by_episode_by_month",
|
||||
$found,
|
||||
600
|
||||
);
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
}
|
||||
|
@ -30,11 +30,11 @@ class AnalyticsPodcastByPlayerModel extends Model
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDataByApp(int $podcastId): array
|
||||
public function getDataByAppWeekly(int $podcastId): array
|
||||
{
|
||||
if (
|
||||
!($found = cache(
|
||||
"{$podcastId}_analytics_podcasts_by_player_by_app"
|
||||
"{$podcastId}_analytics_podcasts_by_player_by_app_weekly"
|
||||
))
|
||||
) {
|
||||
$found = $this->select('`app` as `labels`')
|
||||
@ -50,92 +50,148 @@ class AnalyticsPodcastByPlayerModel extends Model
|
||||
->findAll(10);
|
||||
|
||||
cache()->save(
|
||||
"{$podcastId}_analytics_podcasts_by_player_by_app",
|
||||
"{$podcastId}_analytics_podcasts_by_player_by_app_weekly",
|
||||
$found,
|
||||
600
|
||||
);
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets device data for a podcast
|
||||
* Gets player data for a podcast
|
||||
*
|
||||
* @param int $podcastId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDataByDevice(int $podcastId): array
|
||||
public function getDataByAppYearly(int $podcastId): array
|
||||
{
|
||||
if (
|
||||
!($found = cache(
|
||||
"{$podcastId}_analytics_podcasts_by_player_by_device"
|
||||
"{$podcastId}_analytics_podcasts_by_player_by_app_yearly"
|
||||
))
|
||||
) {
|
||||
$foundApp = $this->select(
|
||||
'CONCAT_WS("/", `device`, `os`, `app`) as `ids`, `app` as `labels`, CONCAT_WS("/", `device`, `os`) as `parents`'
|
||||
)
|
||||
$found = $this->select('`app` as `labels`')
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`app` !=' => null,
|
||||
'`app` !=' => '',
|
||||
'`bot`' => 0,
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
||||
])
|
||||
->groupBy('`ids`')
|
||||
->groupBy('`labels`')
|
||||
->orderBy('`values`', 'DESC')
|
||||
->findAll();
|
||||
->findAll(10);
|
||||
|
||||
$foundOs = $this->select(
|
||||
'CONCAT_WS("/", `device`, `os`) as `ids`, `os` as `labels`, `device` as `parents`'
|
||||
)
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`os` !=' => null,
|
||||
'`bot`' => 0,
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||
])
|
||||
->groupBy('`ids`')
|
||||
->orderBy('`values`', 'DESC')
|
||||
->findAll();
|
||||
|
||||
$foundDevice = $this->select(
|
||||
'`device` as `ids`, `device` as `labels`, "" as `parents`'
|
||||
)
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`device` !=' => null,
|
||||
'`bot`' => 0,
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||
])
|
||||
->groupBy('`ids`')
|
||||
->orderBy('`values`', 'DESC')
|
||||
->findAll();
|
||||
|
||||
$foundBot = $this->select(
|
||||
'"bots" as `ids`, "Bots" as `labels`, "" as `parents`'
|
||||
)
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`bot`' => 1,
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||
])
|
||||
->groupBy('`ids`')
|
||||
->orderBy('`values`', 'DESC')
|
||||
->findAll();
|
||||
|
||||
$found = array_merge($foundApp, $foundOs, $foundDevice, $foundBot);
|
||||
cache()->save(
|
||||
"{$podcastId}_analytics_podcasts_by_player_by_device",
|
||||
"{$podcastId}_analytics_podcasts_by_player_by_app_yearly",
|
||||
$found,
|
||||
600
|
||||
);
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets os data for a podcast
|
||||
*
|
||||
* @param int $podcastId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDataByOsWeekly(int $podcastId): array
|
||||
{
|
||||
if (
|
||||
!($found = cache(
|
||||
"{$podcastId}_analytics_podcasts_by_player_by_os_weekly"
|
||||
))
|
||||
) {
|
||||
$found = $this->select('`os` as `labels`')
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`app` !=' => '',
|
||||
'`bot`' => 0,
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||
])
|
||||
->groupBy('`labels`')
|
||||
->orderBy('`values`', 'DESC')
|
||||
->findAll(10);
|
||||
|
||||
cache()->save(
|
||||
"{$podcastId}_analytics_podcasts_by_player_by_os_weekly",
|
||||
$found,
|
||||
600
|
||||
);
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets player data for a podcast
|
||||
*
|
||||
* @param int $podcastId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDataByDeviceWeekly(int $podcastId): array
|
||||
{
|
||||
if (
|
||||
!($found = cache(
|
||||
"{$podcastId}_analytics_podcasts_by_player_by_device_weekly"
|
||||
))
|
||||
) {
|
||||
$found = $this->select('`device` as `labels`')
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`device` !=' => '',
|
||||
'`bot`' => 0,
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||
])
|
||||
->groupBy('`labels`')
|
||||
->orderBy('`values`', 'DESC')
|
||||
->findAll(10);
|
||||
|
||||
cache()->save(
|
||||
"{$podcastId}_analytics_podcasts_by_player_by_device_weekly",
|
||||
$found,
|
||||
600
|
||||
);
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets bots data for a podcast
|
||||
*
|
||||
* @param int $podcastId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDataBots(int $podcastId): array
|
||||
{
|
||||
if (
|
||||
!($found = cache("{$podcastId}_analytics_podcasts_by_player_bots"))
|
||||
) {
|
||||
$found = $this->select('DATE_FORMAT(`date`,"%Y-%m-01") as `labels`')
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`bot`' => 1,
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
||||
])
|
||||
->groupBy('`labels`')
|
||||
->orderBy('`labels`', 'ASC')
|
||||
->findAll(10);
|
||||
|
||||
cache()->save(
|
||||
"{$podcastId}_analytics_podcasts_by_player_bots",
|
||||
$found,
|
||||
600
|
||||
);
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
}
|
||||
|
@ -32,11 +32,16 @@ class AnalyticsPodcastByRegionModel extends Model
|
||||
*/
|
||||
public function getData(int $podcastId): array
|
||||
{
|
||||
if (!($found = cache("{$podcastId}_analytics_podcast_by_region"))) {
|
||||
$locale = service('request')->getLocale();
|
||||
if (
|
||||
!($found = cache(
|
||||
"{$podcastId}_analytics_podcast_by_region_{$locale}"
|
||||
))
|
||||
) {
|
||||
$found = $this->select(
|
||||
'`country_code`, `region_code`, `latitude`, `longitude`'
|
||||
)
|
||||
->selectSum('`hits`', '`values`')
|
||||
->selectSum('`hits`', '`value`')
|
||||
->groupBy(
|
||||
'`country_code`, `region_code`, `latitude`, `longitude`'
|
||||
)
|
||||
@ -44,11 +49,11 @@ class AnalyticsPodcastByRegionModel extends Model
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||
])
|
||||
->orderBy('`values`', 'DESC')
|
||||
->orderBy('`value`', 'DESC')
|
||||
->findAll();
|
||||
|
||||
cache()->save(
|
||||
"{$podcastId}_analytics_podcast_by_region",
|
||||
"{$podcastId}_analytics_podcast_by_region_{$locale}",
|
||||
$found,
|
||||
600
|
||||
);
|
||||
|
@ -36,7 +36,7 @@ class AnalyticsPodcastModel extends Model
|
||||
$found = $this->select('`date` as `labels`, `hits` as `values`')
|
||||
->where([
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
||||
'`date` >' => date('Y-m-d', strtotime('-60 days')),
|
||||
])
|
||||
->orderBy('`labels`', 'ASC')
|
||||
->findAll();
|
||||
@ -60,7 +60,6 @@ class AnalyticsPodcastModel extends Model
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
||||
])
|
||||
->groupBy('`labels`')
|
||||
->orderBy('`labels`', 'ASC')
|
||||
@ -94,7 +93,7 @@ class AnalyticsPodcastModel extends Model
|
||||
)
|
||||
->where([
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
||||
'`date` >' => date('Y-m-d', strtotime('-60 days')),
|
||||
])
|
||||
->orderBy('`labels`', 'ASC')
|
||||
->findAll();
|
||||
|
@ -59,9 +59,11 @@ class AnalyticsWebsiteByRefererModel extends Model
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDataByDomain(int $podcastId): array
|
||||
public function getDataByDomainWeekly(int $podcastId): array
|
||||
{
|
||||
if (!($found = cache("{$podcastId}_analytics_website_by_domain"))) {
|
||||
if (
|
||||
!($found = cache("{$podcastId}_analytics_website_by_domain_weekly"))
|
||||
) {
|
||||
$found = $this->select('`domain` as `labels`')
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
@ -73,7 +75,38 @@ class AnalyticsWebsiteByRefererModel extends Model
|
||||
->findAll(10);
|
||||
|
||||
cache()->save(
|
||||
"{$podcastId}_analytics_website_by_domain",
|
||||
"{$podcastId}_analytics_website_by_domain_weekly",
|
||||
$found,
|
||||
600
|
||||
);
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets domain data for a podcast
|
||||
*
|
||||
* @param int $podcastId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDataByDomainYearly(int $podcastId): array
|
||||
{
|
||||
if (
|
||||
!($found = cache("{$podcastId}_analytics_website_by_domain_yearly"))
|
||||
) {
|
||||
$found = $this->select('`domain` as `labels`')
|
||||
->selectSum('`hits`', '`values`')
|
||||
->where([
|
||||
'`podcast_id`' => $podcastId,
|
||||
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
||||
])
|
||||
->groupBy('`labels`')
|
||||
->orderBy('`values`', 'DESC')
|
||||
->findAll(10);
|
||||
|
||||
cache()->save(
|
||||
"{$podcastId}_analytics_website_by_domain_yearly",
|
||||
$found,
|
||||
600
|
||||
);
|
||||
|
@ -1,33 +1,29 @@
|
||||
// Import modules
|
||||
import am4geodata_worldLow from "@amcharts/amcharts4-geodata/worldLow";
|
||||
import * as am4charts from "@amcharts/amcharts4/charts";
|
||||
import * as am4core from "@amcharts/amcharts4/core";
|
||||
import * as am4maps from "@amcharts/amcharts4/maps";
|
||||
import am4themes_material from "@amcharts/amcharts4/themes/material";
|
||||
|
||||
const drawPieChart = (chartDivId: string, dataUrl: string | null): void => {
|
||||
// Create chart instance
|
||||
const chart = am4core.create(chartDivId, am4charts.PieChart);
|
||||
am4core.percent(100);
|
||||
|
||||
// Set theme
|
||||
am4core.useTheme(am4themes_material);
|
||||
|
||||
chart.innerRadius = am4core.percent(10);
|
||||
|
||||
// Add data
|
||||
chart.dataSource.url = dataUrl || "";
|
||||
chart.dataSource.parser.options.emptyAs = 0;
|
||||
|
||||
// Add and configure Series
|
||||
const pieSeries = chart.series.push(new am4charts.PieSeries());
|
||||
pieSeries.dataFields.value = "values";
|
||||
pieSeries.dataFields.category = "labels";
|
||||
|
||||
pieSeries.slices.template.stroke = am4core.color("#ffffff");
|
||||
pieSeries.slices.template.strokeWidth = 1;
|
||||
pieSeries.slices.template.strokeOpacity = 1;
|
||||
pieSeries.labels.template.disabled = true;
|
||||
pieSeries.ticks.template.disabled = true;
|
||||
|
||||
chart.legend = new am4charts.Legend();
|
||||
chart.legend.position = "right";
|
||||
chart.legend.scrollable = true;
|
||||
@ -37,32 +33,32 @@ const drawXYChart = (chartDivId: string, dataUrl: string | null): void => {
|
||||
// Create chart instance
|
||||
const chart = am4core.create(chartDivId, am4charts.XYChart);
|
||||
am4core.percent(100);
|
||||
|
||||
// Set theme
|
||||
am4core.useTheme(am4themes_material);
|
||||
|
||||
// Create axes
|
||||
const dateAxis = chart.xAxes.push(new am4charts.DateAxis());
|
||||
dateAxis.renderer.minGridDistance = 60;
|
||||
|
||||
chart.yAxes.push(new am4charts.ValueAxis());
|
||||
|
||||
// Add data
|
||||
chart.dataSource.url = dataUrl || "";
|
||||
chart.dataSource.parser.options.emptyAs = 0;
|
||||
|
||||
// Create series
|
||||
const series = chart.series.push(new am4charts.LineSeries());
|
||||
series.dataFields.valueY = "values";
|
||||
series.dataFields.dateX = "labels";
|
||||
series.tooltipText = "{valueY} downloads";
|
||||
|
||||
series.tooltipText = "{valueY} hits";
|
||||
series.strokeWidth = 2;
|
||||
// Make bullets grow on hover
|
||||
const bullet = series.bullets.push(new am4charts.CircleBullet());
|
||||
bullet.circle.strokeWidth = 2;
|
||||
bullet.circle.radius = 4;
|
||||
bullet.circle.fill = am4core.color("#fff");
|
||||
const bullethover = bullet.states.create("hover");
|
||||
bullethover.properties.scale = 1.3;
|
||||
series.tooltip.pointerOrientation = "vertical";
|
||||
|
||||
chart.cursor = new am4charts.XYCursor();
|
||||
chart.cursor.snapToSeries = series;
|
||||
chart.cursor.xAxis = dateAxis;
|
||||
|
||||
chart.scrollbarX = new am4core.Scrollbar();
|
||||
};
|
||||
|
||||
@ -73,40 +69,74 @@ const drawXYSeriesChart = (
|
||||
// Create chart instance
|
||||
const chart = am4core.create(chartDivId, am4charts.XYChart);
|
||||
am4core.percent(100);
|
||||
|
||||
// Set theme
|
||||
am4core.useTheme(am4themes_material);
|
||||
|
||||
// Create axes
|
||||
chart.xAxes.push(new am4charts.ValueAxis());
|
||||
chart.yAxes.push(new am4charts.ValueAxis());
|
||||
|
||||
// Add data
|
||||
chart.dataSource.url = dataUrl || "";
|
||||
chart.dataSource.parser.options.emptyAs = 0;
|
||||
|
||||
// Create series
|
||||
const series1 = chart.series.push(new am4charts.LineSeries());
|
||||
series1.dataFields.valueX = "X";
|
||||
series1.dataFields.valueY = "aY";
|
||||
|
||||
const series2 = chart.series.push(new am4charts.LineSeries());
|
||||
series2.dataFields.valueX = "X";
|
||||
series2.dataFields.valueY = "bY";
|
||||
|
||||
const series3 = chart.series.push(new am4charts.LineSeries());
|
||||
series3.dataFields.valueX = "X";
|
||||
series3.dataFields.valueY = "cY";
|
||||
|
||||
const series4 = chart.series.push(new am4charts.LineSeries());
|
||||
series4.dataFields.valueX = "X";
|
||||
series4.dataFields.valueY = "dY";
|
||||
|
||||
const series5 = chart.series.push(new am4charts.LineSeries());
|
||||
series5.dataFields.valueX = "X";
|
||||
series5.dataFields.valueY = "eY";
|
||||
};
|
||||
|
||||
const drawMapChart = (chartDivId: string, dataUrl: string | null): void => {
|
||||
// Create map instance
|
||||
const chart = am4core.create(chartDivId, am4maps.MapChart);
|
||||
am4core.percent(100);
|
||||
// Set theme
|
||||
am4core.useTheme(am4themes_material);
|
||||
// Set map definition
|
||||
chart.geodata = am4geodata_worldLow;
|
||||
// Set projection
|
||||
chart.projection = new am4maps.projections.Miller();
|
||||
// Create map polygon series
|
||||
const polygonSeries = chart.series.push(new am4maps.MapPolygonSeries());
|
||||
// Exclude Antartica
|
||||
polygonSeries.exclude = ["AQ"];
|
||||
// Make map load polygon (like country names) data from GeoJSON
|
||||
polygonSeries.useGeodata = true;
|
||||
// Configure series
|
||||
const polygonTemplate = polygonSeries.mapPolygons.template;
|
||||
polygonTemplate.tooltipText = "{name}";
|
||||
polygonTemplate.polygon.fillOpacity = 0.6;
|
||||
// Create hover state and set alternative fill color
|
||||
const hs = polygonTemplate.states.create("hover");
|
||||
hs.properties.fill = chart.colors.getIndex(0);
|
||||
// Add image series
|
||||
const imageSeries = chart.series.push(new am4maps.MapImageSeries());
|
||||
imageSeries.dataSource.url = dataUrl || "";
|
||||
imageSeries.mapImages.template.propertyFields.longitude = "longitude";
|
||||
imageSeries.mapImages.template.propertyFields.latitude = "latitude";
|
||||
imageSeries.mapImages.template.tooltipText =
|
||||
"{country_code}, {region_code}:\n[bold]{value}[/] hits";
|
||||
const circle = imageSeries.mapImages.template.createChild(am4core.Circle);
|
||||
circle.radius = 1;
|
||||
circle.fill = am4core.color("#60f");
|
||||
imageSeries.heatRules.push({
|
||||
target: circle,
|
||||
property: "radius",
|
||||
min: 0.5,
|
||||
max: 3,
|
||||
dataField: "value",
|
||||
});
|
||||
};
|
||||
|
||||
const DrawCharts = (): void => {
|
||||
const chartDivs: NodeListOf<HTMLDivElement> = document.querySelectorAll(
|
||||
"div[data-chart-type]"
|
||||
@ -125,6 +155,9 @@ const DrawCharts = (): void => {
|
||||
case "xy-series-chart":
|
||||
drawXYSeriesChart(chartDiv.id, chartDiv.getAttribute("data-chart-url"));
|
||||
break;
|
||||
case "map-chart":
|
||||
drawMapChart(chartDiv.id, chartDiv.getAttribute("data-chart-url"));
|
||||
break;
|
||||
default:
|
||||
console.error("Unknown chart type:" + chartType);
|
||||
}
|
||||
|
15
app/Views/_assets/styles/charts.css
Normal file
15
app/Views/_assets/styles/charts.css
Normal file
@ -0,0 +1,15 @@
|
||||
.chart-map {
|
||||
height: 800px;
|
||||
border: solid 10px #eee;
|
||||
}
|
||||
.chart-pie {
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
border: solid 1px #eee;
|
||||
}
|
||||
.chart-xy {
|
||||
height: 500px;
|
||||
width: 100%;
|
||||
border: solid 1px #eee;
|
||||
border: solid 3px #eee;
|
||||
}
|
@ -5,3 +5,4 @@
|
||||
@import "./radioBtn.css";
|
||||
@import "./switch.css";
|
||||
@import "./enclosureInput.css";
|
||||
@import "./charts.css";
|
||||
|
@ -9,7 +9,7 @@
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-full max-w-sm mb-6 md:mr-4">
|
||||
<img
|
||||
@ -46,4 +46,29 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mb-12 text-center">
|
||||
<h2><?= lang('Charts.episode_by_day') ?></h2>
|
||||
<div class="chart-xy" id="by-day-graph" data-chart-type="xy-chart" data-chart-url="<?= route_to(
|
||||
'analytics-filtered-data',
|
||||
$podcast->id,
|
||||
'PodcastByEpisode',
|
||||
'ByDay',
|
||||
$episode->id
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-12 text-center">
|
||||
<h2><?= lang('Charts.episode_by_month') ?></h2>
|
||||
<div class="chart-xy" id="by-month-graph" data-chart-type="xy-chart" data-chart-url="<?= route_to(
|
||||
'analytics-filtered-data',
|
||||
$podcast->id,
|
||||
'PodcastByEpisode',
|
||||
'ByMonth',
|
||||
$episode->id
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="/assets/charts.js" type="module"></script>
|
||||
<?= $this->endSection() ?>
|
||||
|
@ -10,7 +10,13 @@ $podcastNavigation = [
|
||||
],
|
||||
'analytics' => [
|
||||
'icon' => 'line-chart',
|
||||
'items' => ['podcast-analytics'],
|
||||
'items' => [
|
||||
'podcast-analytics',
|
||||
'podcast-analytics-unique-listeners',
|
||||
'podcast-analytics-players',
|
||||
'podcast-analytics-locations',
|
||||
'podcast-analytics-webpages',
|
||||
],
|
||||
],
|
||||
'contributors' => [
|
||||
'icon' => 'group',
|
||||
|
@ -1,84 +0,0 @@
|
||||
<?= $this->extend('admin/_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= $podcast->title ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('pageTitle') ?>
|
||||
<?= $podcast->title ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<h2><?= lang('Charts.podcast_by_day') ?></h2>
|
||||
<div class="h-64" id="by-day-graph" data-chart-type="xy-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'Podcast',
|
||||
'ByDay'
|
||||
) ?>"></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(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'PodcastByEpisode',
|
||||
'ByDay'
|
||||
) ?>"></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>
|
||||
<?= $this->endSection() ?>
|
43
app/Views/admin/podcast/analytics/index.php
Normal file
43
app/Views/admin/podcast/analytics/index.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?= $this->extend('admin/_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= $podcast->title ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('pageTitle') ?>
|
||||
<?= $podcast->title ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
<div class="mb-12 text-center">
|
||||
<h2><?= lang('Charts.podcast_by_day') ?></h2>
|
||||
<div class="chart-xy" id="by-day-graph" data-chart-type="xy-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'Podcast',
|
||||
'ByDay'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-12 text-center">
|
||||
<h2><?= lang('Charts.podcast_by_month') ?></h2>
|
||||
<div class="chart-xy" id="by-month-graph" data-chart-type="xy-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'Podcast',
|
||||
'ByMonth'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-12 text-center">
|
||||
<h2><?= lang('Charts.episodes_by_day') ?></h2>
|
||||
<div class="chart-xy" id="by-age-graph" data-chart-type="xy-series-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'PodcastByEpisode',
|
||||
'ByDay'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
<script src="/assets/charts.js" type="module"></script>
|
||||
<?= $this->endSection() ?>
|
46
app/Views/admin/podcast/analytics/locations.php
Normal file
46
app/Views/admin/podcast/analytics/locations.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?= $this->extend('admin/_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= $podcast->title ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('pageTitle') ?>
|
||||
<?= $podcast->title ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<div class="grid grid-cols-2 divide-x">
|
||||
<div class="mb-12 mr-6 text-center">
|
||||
<h2><?= lang('Charts.by_country_weekly') ?></h2>
|
||||
<div class="chart-pie" id="by-country-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'PodcastByCountry',
|
||||
'Weekly'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-12 mr-6 text-center">
|
||||
<h2><?= lang('Charts.by_country_yearly') ?></h2>
|
||||
<div class="chart-pie" id="by-country-by-year-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'PodcastByCountry',
|
||||
'Yearly'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-12 mr-6 text-center">
|
||||
<h2><?= lang('Charts.podcast_by_region') ?></h2>
|
||||
<div class="chart-map" id="by-region-map" data-chart-type="map-chart" data-chart-url="<?= route_to(
|
||||
'analytics-full-data',
|
||||
$podcast->id,
|
||||
'PodcastByRegion'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="/assets/charts.js" type="module"></script>
|
||||
<?= $this->endSection() ?>
|
67
app/Views/admin/podcast/analytics/players.php
Normal file
67
app/Views/admin/podcast/analytics/players.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?= $this->extend('admin/_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= $podcast->title ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('pageTitle') ?>
|
||||
<?= $podcast->title ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<div class="grid grid-cols-2 divide-x">
|
||||
<div class="mb-12 mr-6 text-center">
|
||||
<h2><?= lang('Charts.by_player_weekly') ?></h2>
|
||||
<div class="chart-pie" id="by-app-weekly-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'PodcastByPlayer',
|
||||
'ByAppWeekly'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mb-12 mr-6 text-center">
|
||||
<h2><?= lang('Charts.by_player_yearly') ?></h2>
|
||||
<div class="chart-pie" id="by-app-yearly-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'PodcastByPlayer',
|
||||
'ByAppYearly'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-12 mr-6 text-center">
|
||||
<h2><?= lang('Charts.by_device_weekly') ?></h2>
|
||||
<div class="chart-pie" id="by-device-weekly-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'PodcastByPlayer',
|
||||
'ByDeviceWeekly'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-12 mr-6 text-center">
|
||||
<h2><?= lang('Charts.by_os_weekly') ?></h2>
|
||||
<div class="chart-pie" id="by-os-yearly-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'PodcastByPlayer',
|
||||
'ByOsWeekly'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-12 mr-6 text-center">
|
||||
<h2><?= lang('Charts.podcast_bots') ?></h2>
|
||||
<div class="chart-xy" id="bots-graph" data-chart-type="xy-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'PodcastByPlayer',
|
||||
'Bots'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
<script src="/assets/charts.js" type="module"></script>
|
||||
<?= $this->endSection() ?>
|
34
app/Views/admin/podcast/analytics/unique_listeners.php
Normal file
34
app/Views/admin/podcast/analytics/unique_listeners.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?= $this->extend('admin/_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= $podcast->title ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('pageTitle') ?>
|
||||
<?= $podcast->title ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<div class="mb-12 text-center">
|
||||
<h2><?= lang('Charts.unique_daily_listeners') ?></h2>
|
||||
<div class="chart-xy" id="by-day-listeners-graph" data-chart-type="xy-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'Podcast',
|
||||
'UniqueListenersByDay'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-12 text-center">
|
||||
<h2><?= lang('Charts.unique_monthly_listeners') ?></h2>
|
||||
<div class="chart-xy" id="by-month-listeners-graph" data-chart-type="xy-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'Podcast',
|
||||
'UniqueListenersByMonth'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
<script src="/assets/charts.js" type="module"></script>
|
||||
<?= $this->endSection() ?>
|
61
app/Views/admin/podcast/analytics/webpages.php
Normal file
61
app/Views/admin/podcast/analytics/webpages.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?= $this->extend('admin/_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= $podcast->title ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('pageTitle') ?>
|
||||
<?= $podcast->title ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<div class="grid grid-cols-2 divide-x">
|
||||
|
||||
<div class="mb-12 mr-6 text-center">
|
||||
<h2><?= lang('Charts.by_domain_weekly') ?></h2>
|
||||
<div class="chart-pie" id="by-domain-weekly-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'WebsiteByReferer',
|
||||
'ByDomainWeekly'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mb-12 mr-6 text-center">
|
||||
<h2><?= lang('Charts.by_domain_yearly') ?></h2>
|
||||
<div class="chart-pie" id="by-domain-yearly-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||
'analytics-data',
|
||||
$podcast->id,
|
||||
'WebsiteByReferer',
|
||||
'ByDomainYearly'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mb-12 mr-6 text-center">
|
||||
<h2><?= lang('Charts.by_entry_page') ?></h2>
|
||||
<div class="chart-pie" id="by-entry-page-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||
'analytics-full-data',
|
||||
$podcast->id,
|
||||
'WebsiteByEntryPage'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mb-12 mr-6 text-center">
|
||||
<h2><?= lang('Charts.by_browser') ?></h2>
|
||||
<div class="chart-pie" id="by-browser-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
|
||||
'analytics-full-data',
|
||||
$podcast->id,
|
||||
'WebsiteByBrowser'
|
||||
) ?>"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script src="/assets/charts.js" type="module"></script>
|
||||
<?= $this->endSection() ?>
|
Loading…
x
Reference in New Issue
Block a user