statsController.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * Controller to handle application statistics.
  5. */
  6. class FreshRSS_stats_Controller extends FreshRSS_ActionController {
  7. /**
  8. * @var FreshRSS_ViewStats
  9. */
  10. protected $view;
  11. public function __construct() {
  12. parent::__construct(FreshRSS_ViewStats::class);
  13. }
  14. /**
  15. * This action is called before every other action in that class. It is
  16. * the common boilerplate for every action. It is triggered by the
  17. * underlying framework.
  18. */
  19. public function firstAction(): void {
  20. if (!FreshRSS_Auth::hasAccess()) {
  21. Minz_Error::error(403);
  22. }
  23. $this->_csp([
  24. 'default-src' => "'self'",
  25. 'img-src' => '* data:',
  26. 'style-src' => "'self' 'unsafe-inline'",
  27. ]);
  28. $catDAO = FreshRSS_Factory::createCategoryDao();
  29. $catDAO->checkDefault();
  30. $this->view->categories = $catDAO->listSortedCategories(false) ?: [];
  31. FreshRSS_View::prependTitle(_t('admin.stats.title') . ' · ');
  32. }
  33. /**
  34. * This action handles the statistic main page.
  35. *
  36. * It displays the statistic main page.
  37. * The values computed to display the page are:
  38. * - repartition of read/unread/favorite/not favorite (repartition)
  39. * - number of article per day (entryCount)
  40. * - number of feed by category (feedByCategory)
  41. * - number of article by category (entryByCategory)
  42. * - list of most prolific feed (topFeed)
  43. */
  44. public function indexAction(): void {
  45. $statsDAO = FreshRSS_Factory::createStatsDAO();
  46. FreshRSS_View::appendScript(Minz_Url::display('/scripts/vendor/chart.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/vendor/chart.min.js')));
  47. $this->view->repartitions = $statsDAO->calculateEntryRepartition();
  48. $entryCount = $statsDAO->calculateEntryCount();
  49. if (count($entryCount) > 0) {
  50. $this->view->entryCount = $entryCount;
  51. $this->view->average = round(array_sum(array_values($entryCount)) / count($entryCount), 2);
  52. } else {
  53. $this->view->entryCount = [];
  54. $this->view->average = -1.0;
  55. }
  56. $feedByCategory = [];
  57. $feedByCategory_calculated = $statsDAO->calculateFeedByCategory();
  58. for ($i = 0; $i < count($feedByCategory_calculated); $i++) {
  59. $feedByCategory['label'][$i] = $feedByCategory_calculated[$i]['label'];
  60. $feedByCategory['data'][$i] = $feedByCategory_calculated[$i]['data'];
  61. }
  62. $this->view->feedByCategory = $feedByCategory;
  63. $entryByCategory = [];
  64. $entryByCategory_calculated = $statsDAO->calculateEntryByCategory();
  65. for ($i = 0; $i < count($entryByCategory_calculated); $i++) {
  66. $entryByCategory['label'][$i] = $entryByCategory_calculated[$i]['label'];
  67. $entryByCategory['data'][$i] = $entryByCategory_calculated[$i]['data'];
  68. }
  69. $this->view->entryByCategory = $entryByCategory;
  70. $this->view->topFeed = $statsDAO->calculateTopFeed();
  71. $last30DaysLabels = [];
  72. for ($i = 0; $i < 30; $i++) {
  73. $last30DaysLabels[$i] = date('d.m.Y', strtotime((-30 + $i) . ' days') ?: null);
  74. }
  75. $this->view->last30DaysLabels = $last30DaysLabels;
  76. }
  77. /**
  78. * This action handles the feed action on the idle statistic page.
  79. * set the 'from' parameter to remember that it had a redirection coming from stats controller,
  80. * to use the subscription controller to save it,
  81. * but shows the stats idle page
  82. */
  83. public function feedAction(): void {
  84. $id = Minz_Request::paramInt('id');
  85. $ajax = Minz_Request::paramBoolean('ajax');
  86. if ($ajax) {
  87. $url_redirect = ['c' => 'subscription', 'a' => 'feed', 'params' => ['id' => (string)$id, 'from' => 'stats', 'ajax' => (string)$ajax]];
  88. } else {
  89. $url_redirect = ['c' => 'subscription', 'a' => 'feed', 'params' => ['id' => (string)$id, 'from' => 'stats']];
  90. }
  91. Minz_Request::forward($url_redirect, true);
  92. }
  93. /**
  94. * This action handles the idle feed statistic page.
  95. *
  96. * It displays the list of idle feed for different period. The supported
  97. * periods are:
  98. * - last 5 years
  99. * - last 3 years
  100. * - last 2 years
  101. * - last year
  102. * - last 6 months
  103. * - last 3 months
  104. * - last month
  105. * - last week
  106. */
  107. public function idleAction(): void {
  108. FreshRSS_View::appendScript(Minz_Url::display('/scripts/feed.js?' . @filemtime(PUBLIC_PATH . '/scripts/feed.js')));
  109. $feed_dao = FreshRSS_Factory::createFeedDao();
  110. $statsDAO = FreshRSS_Factory::createStatsDAO();
  111. $feeds = $statsDAO->calculateFeedLastDate() ?: [];
  112. $idleFeeds = [
  113. 'last_5_year' => [],
  114. 'last_3_year' => [],
  115. 'last_2_year' => [],
  116. 'last_year' => [],
  117. 'last_6_month' => [],
  118. 'last_3_month' => [],
  119. 'last_month' => [],
  120. 'last_week' => [],
  121. ];
  122. $now = new \DateTime();
  123. $feedDate = clone $now;
  124. $lastWeek = clone $now;
  125. $lastWeek->modify('-1 week');
  126. $lastMonth = clone $now;
  127. $lastMonth->modify('-1 month');
  128. $last3Month = clone $now;
  129. $last3Month->modify('-3 month');
  130. $last6Month = clone $now;
  131. $last6Month->modify('-6 month');
  132. $lastYear = clone $now;
  133. $lastYear->modify('-1 year');
  134. $last2Year = clone $now;
  135. $last2Year->modify('-2 year');
  136. $last3Year = clone $now;
  137. $last3Year->modify('-3 year');
  138. $last5Year = clone $now;
  139. $last5Year->modify('-5 year');
  140. foreach ($feeds as $feed) {
  141. $feedDAO = FreshRSS_Factory::createFeedDao();
  142. $feedObject = $feedDAO->searchById($feed['id']);
  143. if ($feedObject !== null) {
  144. $feed['favicon'] = $feedObject->favicon();
  145. }
  146. $feedDate->setTimestamp($feed['last_date']);
  147. if ($feedDate >= $lastWeek) {
  148. continue;
  149. }
  150. if ($feedDate < $last5Year) {
  151. $idleFeeds['last_5_year'][] = $feed;
  152. } elseif ($feedDate < $last3Year) {
  153. $idleFeeds['last_3_year'][] = $feed;
  154. } elseif ($feedDate < $last2Year) {
  155. $idleFeeds['last_2_year'][] = $feed;
  156. } elseif ($feedDate < $lastYear) {
  157. $idleFeeds['last_year'][] = $feed;
  158. } elseif ($feedDate < $last6Month) {
  159. $idleFeeds['last_6_month'][] = $feed;
  160. } elseif ($feedDate < $last3Month) {
  161. $idleFeeds['last_3_month'][] = $feed;
  162. } elseif ($feedDate < $lastMonth) {
  163. $idleFeeds['last_month'][] = $feed;
  164. } elseif ($feedDate < $lastWeek) {
  165. $idleFeeds['last_week'][] = $feed;
  166. }
  167. }
  168. $this->view->idleFeeds = $idleFeeds;
  169. $this->view->feeds = $feed_dao->listFeeds();
  170. $id = Minz_Request::paramInt('id');
  171. $this->view->displaySlider = false;
  172. if ($id !== 0) {
  173. $this->view->displaySlider = true;
  174. $feedDAO = FreshRSS_Factory::createFeedDao();
  175. $this->view->feed = $feedDAO->searchById($id);
  176. }
  177. }
  178. /**
  179. * This action handles the article repartition statistic page.
  180. *
  181. * It displays the number of article and the average of article for the
  182. * following periods:
  183. * - hour of the day
  184. * - day of the week
  185. * - month
  186. *
  187. * @todo verify that the metrics used here make some sense. Especially
  188. * for the average.
  189. */
  190. public function repartitionAction(): void {
  191. $statsDAO = FreshRSS_Factory::createStatsDAO();
  192. $categoryDAO = FreshRSS_Factory::createCategoryDao();
  193. $feedDAO = FreshRSS_Factory::createFeedDao();
  194. FreshRSS_View::appendScript(Minz_Url::display('/scripts/vendor/chart.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/vendor/chart.min.js')));
  195. $id = Minz_Request::paramInt('id');
  196. if ($id === 0) {
  197. $id = null;
  198. }
  199. $this->view->categories = $categoryDAO->listCategories(true) ?: [];
  200. $this->view->feed = $id === null ? null : $feedDAO->searchById($id);
  201. $this->view->days = $statsDAO->getDays();
  202. $this->view->months = $statsDAO->getMonths();
  203. $this->view->repartition = $statsDAO->calculateEntryRepartitionPerFeed($id);
  204. $this->view->repartitionHour = $statsDAO->calculateEntryRepartitionPerFeedPerHour($id);
  205. $this->view->averageHour = $statsDAO->calculateEntryAveragePerFeedPerHour($id);
  206. $this->view->repartitionDayOfWeek = $statsDAO->calculateEntryRepartitionPerFeedPerDayOfWeek($id);
  207. $this->view->averageDayOfWeek = $statsDAO->calculateEntryAveragePerFeedPerDayOfWeek($id);
  208. $this->view->repartitionMonth = $statsDAO->calculateEntryRepartitionPerFeedPerMonth($id);
  209. $this->view->averageMonth = $statsDAO->calculateEntryAveragePerFeedPerMonth($id);
  210. $hours24Labels = [];
  211. for ($i = 0; $i < 24; $i++) {
  212. $hours24Labels[$i] = $i . ':xx';
  213. }
  214. $this->view->hours24Labels = $hours24Labels;
  215. }
  216. }