StatsDAO.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <?php
  2. class FreshRSS_StatsDAO extends Minz_ModelPdo {
  3. const ENTRY_COUNT_PERIOD = 30;
  4. protected function sqlFloor($s) {
  5. return "FLOOR($s)";
  6. }
  7. /**
  8. * Calculates entry repartition for all feeds and for main stream.
  9. *
  10. * @return array
  11. */
  12. public function calculateEntryRepartition() {
  13. return array(
  14. 'main_stream' => $this->calculateEntryRepartitionPerFeed(null, true),
  15. 'all_feeds' => $this->calculateEntryRepartitionPerFeed(null, false),
  16. );
  17. }
  18. /**
  19. * Calculates entry repartition for the selection.
  20. * The repartition includes:
  21. * - total entries
  22. * - read entries
  23. * - unread entries
  24. * - favorite entries
  25. *
  26. * @param null|integer $feed feed id
  27. * @param boolean $only_main
  28. * @return array
  29. */
  30. public function calculateEntryRepartitionPerFeed($feed = null, $only_main = false) {
  31. $filter = '';
  32. if ($only_main) {
  33. $filter .= 'AND f.priority = 10';
  34. }
  35. if (!is_null($feed)) {
  36. $filter .= "AND e.id_feed = {$feed}";
  37. }
  38. $sql = <<<SQL
  39. SELECT COUNT(1) AS total,
  40. COUNT(1) - SUM(e.is_read) AS count_unreads,
  41. SUM(e.is_read) AS count_reads,
  42. SUM(e.is_favorite) AS count_favorites
  43. FROM `_entry` AS e, `_feed` AS f
  44. WHERE e.id_feed = f.id
  45. {$filter}
  46. SQL;
  47. $stm = $this->pdo->query($sql);
  48. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  49. return $res[0];
  50. }
  51. /**
  52. * Calculates entry count per day on a 30 days period.
  53. *
  54. * @return array
  55. */
  56. public function calculateEntryCount() {
  57. $count = $this->initEntryCountArray();
  58. $midnight = mktime(0, 0, 0);
  59. $oldest = $midnight - (self::ENTRY_COUNT_PERIOD * 86400);
  60. // Get stats per day for the last 30 days
  61. $sqlDay = $this->sqlFloor("(date - $midnight) / 86400");
  62. $sql = <<<SQL
  63. SELECT {$sqlDay} AS day,
  64. COUNT(*) as count
  65. FROM `_entry`
  66. WHERE date >= {$oldest} AND date < {$midnight}
  67. GROUP BY day
  68. ORDER BY day ASC
  69. SQL;
  70. $stm = $this->pdo->query($sql);
  71. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  72. foreach ($res as $value) {
  73. $count[$value['day']] = (int) $value['count'];
  74. }
  75. return $count;
  76. }
  77. /**
  78. * Initialize an array for the entry count.
  79. *
  80. * @return array
  81. */
  82. protected function initEntryCountArray() {
  83. return $this->initStatsArray(-self::ENTRY_COUNT_PERIOD, -1);
  84. }
  85. /**
  86. * Calculates the number of article per hour of the day per feed
  87. *
  88. * @param integer $feed id
  89. * @return string
  90. */
  91. public function calculateEntryRepartitionPerFeedPerHour($feed = null) {
  92. return $this->calculateEntryRepartitionPerFeedPerPeriod('%H', $feed);
  93. }
  94. /**
  95. * Calculates the number of article per day of week per feed
  96. *
  97. * @param integer $feed id
  98. * @return string
  99. */
  100. public function calculateEntryRepartitionPerFeedPerDayOfWeek($feed = null) {
  101. return $this->calculateEntryRepartitionPerFeedPerPeriod('%w', $feed);
  102. }
  103. /**
  104. * Calculates the number of article per month per feed
  105. *
  106. * @param integer $feed
  107. * @return string
  108. */
  109. public function calculateEntryRepartitionPerFeedPerMonth($feed = null) {
  110. return $this->calculateEntryRepartitionPerFeedPerPeriod('%m', $feed);
  111. }
  112. /**
  113. * Calculates the number of article per period per feed
  114. *
  115. * @param string $period format string to use for grouping
  116. * @param integer $feed id
  117. * @return string
  118. */
  119. protected function calculateEntryRepartitionPerFeedPerPeriod($period, $feed = null) {
  120. $restrict = '';
  121. if ($feed) {
  122. $restrict = "WHERE e.id_feed = {$feed}";
  123. }
  124. $sql = <<<SQL
  125. SELECT DATE_FORMAT(FROM_UNIXTIME(e.date), '{$period}') AS period
  126. , COUNT(1) AS count
  127. FROM `_entry` AS e
  128. {$restrict}
  129. GROUP BY period
  130. ORDER BY period ASC
  131. SQL;
  132. $stm = $this->pdo->query($sql);
  133. $res = $stm->fetchAll(PDO::FETCH_NAMED);
  134. $repartition = array();
  135. foreach ($res as $value) {
  136. $repartition[(int) $value['period']] = (int) $value['count'];
  137. }
  138. return $repartition;
  139. }
  140. /**
  141. * Calculates the average number of article per hour per feed
  142. *
  143. * @param integer $feed id
  144. * @return integer
  145. */
  146. public function calculateEntryAveragePerFeedPerHour($feed = null) {
  147. return $this->calculateEntryAveragePerFeedPerPeriod(1 / 24, $feed);
  148. }
  149. /**
  150. * Calculates the average number of article per day of week per feed
  151. *
  152. * @param integer $feed id
  153. * @return integer
  154. */
  155. public function calculateEntryAveragePerFeedPerDayOfWeek($feed = null) {
  156. return $this->calculateEntryAveragePerFeedPerPeriod(7, $feed);
  157. }
  158. /**
  159. * Calculates the average number of article per month per feed
  160. *
  161. * @param integer $feed id
  162. * @return integer
  163. */
  164. public function calculateEntryAveragePerFeedPerMonth($feed = null) {
  165. return $this->calculateEntryAveragePerFeedPerPeriod(30, $feed);
  166. }
  167. /**
  168. * Calculates the average number of article per feed
  169. *
  170. * @param float $period number used to divide the number of day in the period
  171. * @param integer $feed id
  172. * @return integer
  173. */
  174. protected function calculateEntryAveragePerFeedPerPeriod($period, $feed = null) {
  175. $restrict = '';
  176. if ($feed) {
  177. $restrict = "WHERE e.id_feed = {$feed}";
  178. }
  179. $sql = <<<SQL
  180. SELECT COUNT(1) AS count
  181. , MIN(date) AS date_min
  182. , MAX(date) AS date_max
  183. FROM `_entry` AS e
  184. {$restrict}
  185. SQL;
  186. $stm = $this->pdo->query($sql);
  187. $res = $stm->fetch(PDO::FETCH_NAMED);
  188. $date_min = new \DateTime();
  189. $date_min->setTimestamp($res['date_min']);
  190. $date_max = new \DateTime();
  191. $date_max->setTimestamp($res['date_max']);
  192. $interval = $date_max->diff($date_min, true);
  193. $interval_in_days = $interval->format('%a');
  194. if ($interval_in_days <= 0) {
  195. // Surely only one article.
  196. // We will return count / (period/period) == count.
  197. $interval_in_days = $period;
  198. }
  199. return $res['count'] / ($interval_in_days / $period);
  200. }
  201. /**
  202. * Initialize an array for statistics depending on a range
  203. *
  204. * @param integer $min
  205. * @param integer $max
  206. * @return array
  207. */
  208. protected function initStatsArray($min, $max) {
  209. return array_map(function () {
  210. return 0;
  211. }, array_flip(range($min, $max)));
  212. }
  213. /**
  214. * Calculates feed count per category.
  215. * @return array
  216. */
  217. public function calculateFeedByCategory() {
  218. $sql = <<<SQL
  219. SELECT c.name AS label
  220. , COUNT(f.id) AS data
  221. FROM `_category` AS c, `_feed` AS f
  222. WHERE c.id = f.category
  223. GROUP BY label
  224. ORDER BY data DESC
  225. SQL;
  226. $stm = $this->pdo->query($sql);
  227. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  228. return $res;
  229. }
  230. /**
  231. * Calculates entry count per category.
  232. * @return array
  233. */
  234. public function calculateEntryByCategory() {
  235. $sql = <<<SQL
  236. SELECT c.name AS label
  237. , COUNT(e.id) AS data
  238. FROM `_category` AS c, `_feed` AS f, `_entry` AS e
  239. WHERE c.id = f.category
  240. AND f.id = e.id_feed
  241. GROUP BY label
  242. ORDER BY data DESC
  243. SQL;
  244. $stm = $this->pdo->query($sql);
  245. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  246. return $res;
  247. }
  248. /**
  249. * Calculates the 10 top feeds based on their number of entries
  250. *
  251. * @return array
  252. */
  253. public function calculateTopFeed() {
  254. $sql = <<<SQL
  255. SELECT f.id AS id
  256. , MAX(f.name) AS name
  257. , MAX(c.name) AS category
  258. , COUNT(e.id) AS count
  259. FROM `_category` AS c, `_feed` AS f, `_entry` AS e
  260. WHERE c.id = f.category
  261. AND f.id = e.id_feed
  262. GROUP BY f.id
  263. ORDER BY count DESC
  264. LIMIT 10
  265. SQL;
  266. $stm = $this->pdo->query($sql);
  267. return $stm->fetchAll(PDO::FETCH_ASSOC);
  268. }
  269. /**
  270. * Calculates the last publication date for each feed
  271. *
  272. * @return array
  273. */
  274. public function calculateFeedLastDate() {
  275. $sql = <<<SQL
  276. SELECT MAX(f.id) as id
  277. , MAX(f.name) AS name
  278. , MAX(date) AS last_date
  279. , COUNT(*) AS nb_articles
  280. FROM `_feed` AS f, `_entry` AS e
  281. WHERE f.id = e.id_feed
  282. GROUP BY f.id
  283. ORDER BY name
  284. SQL;
  285. $stm = $this->pdo->query($sql);
  286. return $stm->fetchAll(PDO::FETCH_ASSOC);
  287. }
  288. /**
  289. * Gets days ready for graphs
  290. *
  291. * @return string
  292. */
  293. public function getDays() {
  294. return $this->convertToTranslatedJson(array(
  295. 'sun',
  296. 'mon',
  297. 'tue',
  298. 'wed',
  299. 'thu',
  300. 'fri',
  301. 'sat',
  302. ));
  303. }
  304. /**
  305. * Gets months ready for graphs
  306. *
  307. * @return string
  308. */
  309. public function getMonths() {
  310. return $this->convertToTranslatedJson(array(
  311. 'jan',
  312. 'feb',
  313. 'mar',
  314. 'apr',
  315. 'may_',
  316. 'jun',
  317. 'jul',
  318. 'aug',
  319. 'sep',
  320. 'oct',
  321. 'nov',
  322. 'dec',
  323. ));
  324. }
  325. /**
  326. * Translates array content
  327. *
  328. * @param array $data
  329. * @return array
  330. */
  331. private function convertToTranslatedJson($data = array()) {
  332. $translated = array_map(function($a) {
  333. return _t('gen.date.' . $a);
  334. }, $data);
  335. return $translated;
  336. }
  337. }