mirror of
https://code.castopod.org/adaures/castopod
synced 2025-05-24 02:52:01 +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',
|
'as' => 'podcast-delete',
|
||||||
'filter' => 'permission:podcasts-delete',
|
'filter' => 'permission:podcasts-delete',
|
||||||
]);
|
]);
|
||||||
$routes->get('analytics', 'Podcast::analytics/$1', [
|
|
||||||
'as' => 'podcast-analytics',
|
$routes->group('analytics', function ($routes) {
|
||||||
'filter' => 'permission:podcasts-view,podcast-view',
|
$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(
|
$routes->get(
|
||||||
'analytics-data/(:segment)',
|
'analytics-data/(:segment)',
|
||||||
'AnalyticsData::getData/$1/$2',
|
'AnalyticsData::getData/$1/$2',
|
||||||
|
@ -58,12 +58,44 @@ class Podcast extends BaseController
|
|||||||
return view('admin/podcast/view', $data);
|
return view('admin/podcast/view', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function analytics()
|
public function viewAnalytics()
|
||||||
{
|
{
|
||||||
$data = ['podcast' => $this->podcast];
|
$data = ['podcast' => $this->podcast];
|
||||||
|
|
||||||
replace_breadcrumb_params([0 => $this->podcast->title]);
|
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()
|
public function create()
|
||||||
|
@ -20,4 +20,9 @@ class AnalyticsPodcastsByCountry extends Entity
|
|||||||
'date' => 'datetime',
|
'date' => 'datetime',
|
||||||
'hits' => 'integer',
|
'hits' => 'integer',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function getLabels()
|
||||||
|
{
|
||||||
|
return lang('Countries.' . $this->attributes['labels']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,4 +23,9 @@ class AnalyticsPodcastsByRegion extends Entity
|
|||||||
'date' => 'datetime',
|
'date' => 'datetime',
|
||||||
'hits' => 'integer',
|
'hits' => 'integer',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function getCountryCode()
|
||||||
|
{
|
||||||
|
return lang('Countries.' . $this->attributes['country_code']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,4 +20,10 @@ class AnalyticsWebsiteByEntryPage extends Entity
|
|||||||
'date' => 'datetime',
|
'date' => 'datetime',
|
||||||
'hits' => 'integer',
|
'hits' => 'integer',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function getLabels()
|
||||||
|
{
|
||||||
|
$split = explode('/', $this->attributes['labels']);
|
||||||
|
return $split[count($split) - 1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,4 +23,8 @@ return [
|
|||||||
'settings' => 'settings',
|
'settings' => 'settings',
|
||||||
'platforms' => 'platforms',
|
'platforms' => 'platforms',
|
||||||
'analytics' => 'Analytics',
|
'analytics' => 'Analytics',
|
||||||
|
'locations' => 'Locations',
|
||||||
|
'website' => 'Website',
|
||||||
|
'unique-listeners' => 'Unique listeners',
|
||||||
|
'players' => 'Players',
|
||||||
];
|
];
|
||||||
|
@ -7,14 +7,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
return [
|
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_daily_listeners' => 'Daily unique listeners',
|
||||||
'unique_monthly_listeners' => 'Monthly 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_day' => 'Podcast daily downloads',
|
||||||
'podcast_by_month' => 'Podcast monthly 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' =>
|
'episodes_by_day' =>
|
||||||
'5 latest episodes downloads (during their first 60 days)',
|
'5 latest episodes downloads (during their first 60 days)',
|
||||||
'by_country' => 'Podcast downloads by country (for the past week)',
|
'by_country_weekly' => 'Podcast downloads by country (for the past week)',
|
||||||
'by_domain' => 'Website visits by origin (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',
|
'contributor-add' => 'Add contributor',
|
||||||
'settings' => 'Settings',
|
'settings' => 'Settings',
|
||||||
'platforms' => 'Podcast platforms',
|
'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_daily_listeners' => 'Auditeurs uniques quotidiens',
|
||||||
'unique_monthly_listeners' => 'Auditeurs uniques mensuels',
|
'unique_monthly_listeners' => 'Auditeurs uniques mensuels',
|
||||||
'by_browser' =>
|
'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_day' => 'Téléchargements quotidiens de podcasts',
|
||||||
'podcast_by_month' => 'Téléchargements mensuels de podcasts',
|
'podcast_by_month' => 'Téléchargements mensuels de podcasts',
|
||||||
'episode_by_day' =>
|
'episode_by_day' =>
|
||||||
@ -33,10 +33,10 @@ return [
|
|||||||
'by_country_yearly' =>
|
'by_country_yearly' =>
|
||||||
'Téléchargement de podcasts par pays (sur la dernière année)',
|
'Téléchargement de podcasts par pays (sur la dernière année)',
|
||||||
'by_domain_weekly' =>
|
'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' =>
|
'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' =>
|
'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)',
|
'podcast_bots' => 'Robots (bots)',
|
||||||
];
|
];
|
||||||
|
@ -20,8 +20,8 @@ return [
|
|||||||
'contributor-add' => 'Ajouter un contributeur',
|
'contributor-add' => 'Ajouter un contributeur',
|
||||||
'settings' => 'Paramètres',
|
'settings' => 'Paramètres',
|
||||||
'platforms' => 'Plateformes du podcast',
|
'platforms' => 'Plateformes du podcast',
|
||||||
'podcast-analytics' => 'Mesures d’audience',
|
'podcast-analytics' => 'Vue d’ensemble',
|
||||||
'podcast-analytics-website' => 'Visites du site web',
|
'podcast-analytics-webpages' => 'Visites des pages web',
|
||||||
'podcast-analytics-locations' => 'Localisations',
|
'podcast-analytics-locations' => 'Localisations',
|
||||||
'podcast-analytics-unique-listeners' => 'Auditeurs uniques',
|
'podcast-analytics-unique-listeners' => 'Auditeurs uniques',
|
||||||
'podcast-analytics-players' => 'Lecteurs',
|
'podcast-analytics-players' => 'Lecteurs',
|
||||||
|
@ -30,9 +30,14 @@ class AnalyticsPodcastByCountryModel extends Model
|
|||||||
*
|
*
|
||||||
* @return array
|
* @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`')
|
$found = $this->select('`country_code` as `labels`')
|
||||||
->selectSum('`hits`', '`values`')
|
->selectSum('`hits`', '`values`')
|
||||||
->where([
|
->where([
|
||||||
@ -44,7 +49,41 @@ class AnalyticsPodcastByCountryModel extends Model
|
|||||||
->findAll(10);
|
->findAll(10);
|
||||||
|
|
||||||
cache()->save(
|
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,
|
$found,
|
||||||
600
|
600
|
||||||
);
|
);
|
||||||
|
@ -37,12 +37,12 @@ class AnalyticsPodcastByEpisodeModel extends Model
|
|||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
$lastEpisodes = (new EpisodeModel())
|
$lastEpisodes = (new EpisodeModel())
|
||||||
->select('id, season_number, number, title')
|
->select('`id`, `season_number`, `number`, `title`')
|
||||||
->orderBy('id', 'DESC')
|
->orderBy('`id`', 'DESC')
|
||||||
->where(['podcast_id' => $podcastId])
|
->where(['`podcast_id`' => $podcastId])
|
||||||
->findAll(5);
|
->findAll(5);
|
||||||
|
|
||||||
$found = $this->select('age AS X');
|
$found = $this->select('`age` AS `X`');
|
||||||
|
|
||||||
$letter = 97;
|
$letter = 97;
|
||||||
foreach ($lastEpisodes as $episode) {
|
foreach ($lastEpisodes as $episode) {
|
||||||
@ -51,7 +51,7 @@ class AnalyticsPodcastByEpisodeModel extends Model
|
|||||||
'(CASE WHEN `episode_id`=' .
|
'(CASE WHEN `episode_id`=' .
|
||||||
$episode->id .
|
$episode->id .
|
||||||
' THEN `hits` END)',
|
' THEN `hits` END)',
|
||||||
chr($letter) . 'Y'
|
'`' . chr($letter) . 'Y`'
|
||||||
)
|
)
|
||||||
->select(
|
->select(
|
||||||
'"' .
|
'"' .
|
||||||
@ -62,20 +62,20 @@ class AnalyticsPodcastByEpisodeModel extends Model
|
|||||||
? ''
|
? ''
|
||||||
: '-' . $episode->number . '/ ') .
|
: '-' . $episode->number . '/ ') .
|
||||||
$episode->title .
|
$episode->title .
|
||||||
'" AS ' .
|
'" AS `' .
|
||||||
chr($letter) .
|
chr($letter) .
|
||||||
'Value'
|
'Value`'
|
||||||
);
|
);
|
||||||
$letter++;
|
$letter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
$found = $found
|
$found = $found
|
||||||
->where([
|
->where([
|
||||||
'podcast_id' => $podcastId,
|
'`podcast_id`' => $podcastId,
|
||||||
'age <' => 60,
|
'`age` <' => 60,
|
||||||
])
|
])
|
||||||
->groupBy('X')
|
->groupBy('`X`')
|
||||||
->orderBy('X', 'ASC')
|
->orderBy('`X`', 'ASC')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
cache()->save(
|
cache()->save(
|
||||||
@ -91,14 +91,15 @@ class AnalyticsPodcastByEpisodeModel extends Model
|
|||||||
"{$podcastId}_{$episodeId}_analytics_podcast_by_episode_by_day"
|
"{$podcastId}_{$episodeId}_analytics_podcast_by_episode_by_day"
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
$found = $this->select('date as labels')
|
$found = $this->select('`date as `labels`')
|
||||||
->selectSum('hits', 'values')
|
->selectSum('`hits`', '`values`')
|
||||||
->where([
|
->where([
|
||||||
'episode_id' => $episodeId,
|
'`episode_id`' => $episodeId,
|
||||||
'podcast_id' => $podcastId,
|
'`podcast_id`' => $podcastId,
|
||||||
|
'`age` <' => 60,
|
||||||
])
|
])
|
||||||
->groupBy('labels')
|
->groupBy('`labels`')
|
||||||
->orderBy('labels', 'ASC')
|
->orderBy('`labels`', 'ASC')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
cache()->save(
|
cache()->save(
|
||||||
@ -110,4 +111,35 @@ class AnalyticsPodcastByEpisodeModel extends Model
|
|||||||
return $found;
|
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
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getDataByApp(int $podcastId): array
|
public function getDataByAppWeekly(int $podcastId): array
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
!($found = cache(
|
!($found = cache(
|
||||||
"{$podcastId}_analytics_podcasts_by_player_by_app"
|
"{$podcastId}_analytics_podcasts_by_player_by_app_weekly"
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
$found = $this->select('`app` as `labels`')
|
$found = $this->select('`app` as `labels`')
|
||||||
@ -50,92 +50,148 @@ class AnalyticsPodcastByPlayerModel extends Model
|
|||||||
->findAll(10);
|
->findAll(10);
|
||||||
|
|
||||||
cache()->save(
|
cache()->save(
|
||||||
"{$podcastId}_analytics_podcasts_by_player_by_app",
|
"{$podcastId}_analytics_podcasts_by_player_by_app_weekly",
|
||||||
$found,
|
$found,
|
||||||
600
|
600
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $found;
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets device data for a podcast
|
* Gets player data for a podcast
|
||||||
*
|
*
|
||||||
* @param int $podcastId
|
* @param int $podcastId
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getDataByDevice(int $podcastId): array
|
public function getDataByAppYearly(int $podcastId): array
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
!($found = cache(
|
!($found = cache(
|
||||||
"{$podcastId}_analytics_podcasts_by_player_by_device"
|
"{$podcastId}_analytics_podcasts_by_player_by_app_yearly"
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
$foundApp = $this->select(
|
$found = $this->select('`app` as `labels`')
|
||||||
'CONCAT_WS("/", `device`, `os`, `app`) as `ids`, `app` as `labels`, CONCAT_WS("/", `device`, `os`) as `parents`'
|
|
||||||
)
|
|
||||||
->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 year')),
|
||||||
])
|
])
|
||||||
->groupBy('`ids`')
|
->groupBy('`labels`')
|
||||||
->orderBy('`values`', 'DESC')
|
->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(
|
cache()->save(
|
||||||
"{$podcastId}_analytics_podcasts_by_player_by_device",
|
"{$podcastId}_analytics_podcasts_by_player_by_app_yearly",
|
||||||
$found,
|
$found,
|
||||||
600
|
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;
|
return $found;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,11 +32,16 @@ class AnalyticsPodcastByRegionModel extends Model
|
|||||||
*/
|
*/
|
||||||
public function getData(int $podcastId): array
|
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(
|
$found = $this->select(
|
||||||
'`country_code`, `region_code`, `latitude`, `longitude`'
|
'`country_code`, `region_code`, `latitude`, `longitude`'
|
||||||
)
|
)
|
||||||
->selectSum('`hits`', '`values`')
|
->selectSum('`hits`', '`value`')
|
||||||
->groupBy(
|
->groupBy(
|
||||||
'`country_code`, `region_code`, `latitude`, `longitude`'
|
'`country_code`, `region_code`, `latitude`, `longitude`'
|
||||||
)
|
)
|
||||||
@ -44,11 +49,11 @@ class AnalyticsPodcastByRegionModel extends Model
|
|||||||
'`podcast_id`' => $podcastId,
|
'`podcast_id`' => $podcastId,
|
||||||
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
'`date` >' => date('Y-m-d', strtotime('-1 week')),
|
||||||
])
|
])
|
||||||
->orderBy('`values`', 'DESC')
|
->orderBy('`value`', 'DESC')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
cache()->save(
|
cache()->save(
|
||||||
"{$podcastId}_analytics_podcast_by_region",
|
"{$podcastId}_analytics_podcast_by_region_{$locale}",
|
||||||
$found,
|
$found,
|
||||||
600
|
600
|
||||||
);
|
);
|
||||||
|
@ -36,7 +36,7 @@ class AnalyticsPodcastModel extends Model
|
|||||||
$found = $this->select('`date` as `labels`, `hits` as `values`')
|
$found = $this->select('`date` as `labels`, `hits` as `values`')
|
||||||
->where([
|
->where([
|
||||||
'`podcast_id`' => $podcastId,
|
'`podcast_id`' => $podcastId,
|
||||||
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
'`date` >' => date('Y-m-d', strtotime('-60 days')),
|
||||||
])
|
])
|
||||||
->orderBy('`labels`', 'ASC')
|
->orderBy('`labels`', 'ASC')
|
||||||
->findAll();
|
->findAll();
|
||||||
@ -60,7 +60,6 @@ class AnalyticsPodcastModel extends Model
|
|||||||
->selectSum('`hits`', '`values`')
|
->selectSum('`hits`', '`values`')
|
||||||
->where([
|
->where([
|
||||||
'`podcast_id`' => $podcastId,
|
'`podcast_id`' => $podcastId,
|
||||||
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
|
||||||
])
|
])
|
||||||
->groupBy('`labels`')
|
->groupBy('`labels`')
|
||||||
->orderBy('`labels`', 'ASC')
|
->orderBy('`labels`', 'ASC')
|
||||||
@ -94,7 +93,7 @@ class AnalyticsPodcastModel extends Model
|
|||||||
)
|
)
|
||||||
->where([
|
->where([
|
||||||
'`podcast_id`' => $podcastId,
|
'`podcast_id`' => $podcastId,
|
||||||
'`date` >' => date('Y-m-d', strtotime('-1 year')),
|
'`date` >' => date('Y-m-d', strtotime('-60 days')),
|
||||||
])
|
])
|
||||||
->orderBy('`labels`', 'ASC')
|
->orderBy('`labels`', 'ASC')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
@ -59,9 +59,11 @@ class AnalyticsWebsiteByRefererModel extends Model
|
|||||||
*
|
*
|
||||||
* @return array
|
* @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`')
|
$found = $this->select('`domain` as `labels`')
|
||||||
->selectSum('`hits`', '`values`')
|
->selectSum('`hits`', '`values`')
|
||||||
->where([
|
->where([
|
||||||
@ -73,7 +75,38 @@ class AnalyticsWebsiteByRefererModel extends Model
|
|||||||
->findAll(10);
|
->findAll(10);
|
||||||
|
|
||||||
cache()->save(
|
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,
|
$found,
|
||||||
600
|
600
|
||||||
);
|
);
|
||||||
|
@ -1,33 +1,29 @@
|
|||||||
// Import modules
|
// Import modules
|
||||||
|
import am4geodata_worldLow from "@amcharts/amcharts4-geodata/worldLow";
|
||||||
import * as am4charts from "@amcharts/amcharts4/charts";
|
import * as am4charts from "@amcharts/amcharts4/charts";
|
||||||
import * as am4core from "@amcharts/amcharts4/core";
|
import * as am4core from "@amcharts/amcharts4/core";
|
||||||
|
import * as am4maps from "@amcharts/amcharts4/maps";
|
||||||
import am4themes_material from "@amcharts/amcharts4/themes/material";
|
import am4themes_material from "@amcharts/amcharts4/themes/material";
|
||||||
|
|
||||||
const drawPieChart = (chartDivId: string, dataUrl: string | null): void => {
|
const drawPieChart = (chartDivId: string, dataUrl: string | null): void => {
|
||||||
// Create chart instance
|
// Create chart instance
|
||||||
const chart = am4core.create(chartDivId, am4charts.PieChart);
|
const chart = am4core.create(chartDivId, am4charts.PieChart);
|
||||||
am4core.percent(100);
|
am4core.percent(100);
|
||||||
|
|
||||||
// Set theme
|
// Set theme
|
||||||
am4core.useTheme(am4themes_material);
|
am4core.useTheme(am4themes_material);
|
||||||
|
|
||||||
chart.innerRadius = am4core.percent(10);
|
chart.innerRadius = am4core.percent(10);
|
||||||
|
|
||||||
// Add data
|
// Add data
|
||||||
chart.dataSource.url = dataUrl || "";
|
chart.dataSource.url = dataUrl || "";
|
||||||
chart.dataSource.parser.options.emptyAs = 0;
|
chart.dataSource.parser.options.emptyAs = 0;
|
||||||
|
|
||||||
// Add and configure Series
|
// Add and configure Series
|
||||||
const pieSeries = chart.series.push(new am4charts.PieSeries());
|
const pieSeries = chart.series.push(new am4charts.PieSeries());
|
||||||
pieSeries.dataFields.value = "values";
|
pieSeries.dataFields.value = "values";
|
||||||
pieSeries.dataFields.category = "labels";
|
pieSeries.dataFields.category = "labels";
|
||||||
|
|
||||||
pieSeries.slices.template.stroke = am4core.color("#ffffff");
|
pieSeries.slices.template.stroke = am4core.color("#ffffff");
|
||||||
pieSeries.slices.template.strokeWidth = 1;
|
pieSeries.slices.template.strokeWidth = 1;
|
||||||
pieSeries.slices.template.strokeOpacity = 1;
|
pieSeries.slices.template.strokeOpacity = 1;
|
||||||
pieSeries.labels.template.disabled = true;
|
pieSeries.labels.template.disabled = true;
|
||||||
pieSeries.ticks.template.disabled = true;
|
pieSeries.ticks.template.disabled = true;
|
||||||
|
|
||||||
chart.legend = new am4charts.Legend();
|
chart.legend = new am4charts.Legend();
|
||||||
chart.legend.position = "right";
|
chart.legend.position = "right";
|
||||||
chart.legend.scrollable = true;
|
chart.legend.scrollable = true;
|
||||||
@ -37,32 +33,32 @@ const drawXYChart = (chartDivId: string, dataUrl: string | null): void => {
|
|||||||
// Create chart instance
|
// Create chart instance
|
||||||
const chart = am4core.create(chartDivId, am4charts.XYChart);
|
const chart = am4core.create(chartDivId, am4charts.XYChart);
|
||||||
am4core.percent(100);
|
am4core.percent(100);
|
||||||
|
|
||||||
// Set theme
|
// Set theme
|
||||||
am4core.useTheme(am4themes_material);
|
am4core.useTheme(am4themes_material);
|
||||||
|
|
||||||
// Create axes
|
// Create axes
|
||||||
const dateAxis = chart.xAxes.push(new am4charts.DateAxis());
|
const dateAxis = chart.xAxes.push(new am4charts.DateAxis());
|
||||||
dateAxis.renderer.minGridDistance = 60;
|
dateAxis.renderer.minGridDistance = 60;
|
||||||
|
|
||||||
chart.yAxes.push(new am4charts.ValueAxis());
|
chart.yAxes.push(new am4charts.ValueAxis());
|
||||||
|
|
||||||
// Add data
|
// Add data
|
||||||
chart.dataSource.url = dataUrl || "";
|
chart.dataSource.url = dataUrl || "";
|
||||||
chart.dataSource.parser.options.emptyAs = 0;
|
chart.dataSource.parser.options.emptyAs = 0;
|
||||||
|
|
||||||
// Create series
|
// Create series
|
||||||
const series = chart.series.push(new am4charts.LineSeries());
|
const series = chart.series.push(new am4charts.LineSeries());
|
||||||
series.dataFields.valueY = "values";
|
series.dataFields.valueY = "values";
|
||||||
series.dataFields.dateX = "labels";
|
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";
|
series.tooltip.pointerOrientation = "vertical";
|
||||||
|
|
||||||
chart.cursor = new am4charts.XYCursor();
|
chart.cursor = new am4charts.XYCursor();
|
||||||
chart.cursor.snapToSeries = series;
|
chart.cursor.snapToSeries = series;
|
||||||
chart.cursor.xAxis = dateAxis;
|
chart.cursor.xAxis = dateAxis;
|
||||||
|
|
||||||
chart.scrollbarX = new am4core.Scrollbar();
|
chart.scrollbarX = new am4core.Scrollbar();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -73,40 +69,74 @@ const drawXYSeriesChart = (
|
|||||||
// Create chart instance
|
// Create chart instance
|
||||||
const chart = am4core.create(chartDivId, am4charts.XYChart);
|
const chart = am4core.create(chartDivId, am4charts.XYChart);
|
||||||
am4core.percent(100);
|
am4core.percent(100);
|
||||||
|
|
||||||
// Set theme
|
// Set theme
|
||||||
am4core.useTheme(am4themes_material);
|
am4core.useTheme(am4themes_material);
|
||||||
|
|
||||||
// Create axes
|
// Create axes
|
||||||
chart.xAxes.push(new am4charts.ValueAxis());
|
chart.xAxes.push(new am4charts.ValueAxis());
|
||||||
chart.yAxes.push(new am4charts.ValueAxis());
|
chart.yAxes.push(new am4charts.ValueAxis());
|
||||||
|
|
||||||
// Add data
|
// Add data
|
||||||
chart.dataSource.url = dataUrl || "";
|
chart.dataSource.url = dataUrl || "";
|
||||||
chart.dataSource.parser.options.emptyAs = 0;
|
chart.dataSource.parser.options.emptyAs = 0;
|
||||||
|
|
||||||
// Create series
|
// Create series
|
||||||
const series1 = chart.series.push(new am4charts.LineSeries());
|
const series1 = chart.series.push(new am4charts.LineSeries());
|
||||||
series1.dataFields.valueX = "X";
|
series1.dataFields.valueX = "X";
|
||||||
series1.dataFields.valueY = "aY";
|
series1.dataFields.valueY = "aY";
|
||||||
|
|
||||||
const series2 = chart.series.push(new am4charts.LineSeries());
|
const series2 = chart.series.push(new am4charts.LineSeries());
|
||||||
series2.dataFields.valueX = "X";
|
series2.dataFields.valueX = "X";
|
||||||
series2.dataFields.valueY = "bY";
|
series2.dataFields.valueY = "bY";
|
||||||
|
|
||||||
const series3 = chart.series.push(new am4charts.LineSeries());
|
const series3 = chart.series.push(new am4charts.LineSeries());
|
||||||
series3.dataFields.valueX = "X";
|
series3.dataFields.valueX = "X";
|
||||||
series3.dataFields.valueY = "cY";
|
series3.dataFields.valueY = "cY";
|
||||||
|
|
||||||
const series4 = chart.series.push(new am4charts.LineSeries());
|
const series4 = chart.series.push(new am4charts.LineSeries());
|
||||||
series4.dataFields.valueX = "X";
|
series4.dataFields.valueX = "X";
|
||||||
series4.dataFields.valueY = "dY";
|
series4.dataFields.valueY = "dY";
|
||||||
|
|
||||||
const series5 = chart.series.push(new am4charts.LineSeries());
|
const series5 = chart.series.push(new am4charts.LineSeries());
|
||||||
series5.dataFields.valueX = "X";
|
series5.dataFields.valueX = "X";
|
||||||
series5.dataFields.valueY = "eY";
|
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 DrawCharts = (): void => {
|
||||||
const chartDivs: NodeListOf<HTMLDivElement> = document.querySelectorAll(
|
const chartDivs: NodeListOf<HTMLDivElement> = document.querySelectorAll(
|
||||||
"div[data-chart-type]"
|
"div[data-chart-type]"
|
||||||
@ -125,6 +155,9 @@ const DrawCharts = (): void => {
|
|||||||
case "xy-series-chart":
|
case "xy-series-chart":
|
||||||
drawXYSeriesChart(chartDiv.id, chartDiv.getAttribute("data-chart-url"));
|
drawXYSeriesChart(chartDiv.id, chartDiv.getAttribute("data-chart-url"));
|
||||||
break;
|
break;
|
||||||
|
case "map-chart":
|
||||||
|
drawMapChart(chartDiv.id, chartDiv.getAttribute("data-chart-url"));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.error("Unknown chart type:" + chartType);
|
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 "./radioBtn.css";
|
||||||
@import "./switch.css";
|
@import "./switch.css";
|
||||||
@import "./enclosureInput.css";
|
@import "./enclosureInput.css";
|
||||||
|
@import "./charts.css";
|
||||||
|
@ -46,4 +46,29 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</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() ?>
|
<?= $this->endSection() ?>
|
||||||
|
@ -10,7 +10,13 @@ $podcastNavigation = [
|
|||||||
],
|
],
|
||||||
'analytics' => [
|
'analytics' => [
|
||||||
'icon' => 'line-chart',
|
'icon' => 'line-chart',
|
||||||
'items' => ['podcast-analytics'],
|
'items' => [
|
||||||
|
'podcast-analytics',
|
||||||
|
'podcast-analytics-unique-listeners',
|
||||||
|
'podcast-analytics-players',
|
||||||
|
'podcast-analytics-locations',
|
||||||
|
'podcast-analytics-webpages',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'contributors' => [
|
'contributors' => [
|
||||||
'icon' => 'group',
|
'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