StatsDAO.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <?php
  2. class FreshRSS_StatsDAO extends Minz_ModelPdo {
  3. const ENTRY_COUNT_PERIOD = 30;
  4. /**
  5. * Calculates entry repartition for all feeds and for main stream.
  6. *
  7. * @return array
  8. */
  9. public function calculateEntryRepartition() {
  10. return array(
  11. 'main_stream' => $this->calculateEntryRepartitionPerFeed(null, true),
  12. 'all_feeds' => $this->calculateEntryRepartitionPerFeed(null, false),
  13. );
  14. }
  15. /**
  16. * Calculates entry repartition for the selection.
  17. * The repartition includes:
  18. * - total entries
  19. * - read entries
  20. * - unread entries
  21. * - favorite entries
  22. *
  23. * @param null|integer $feed feed id
  24. * @param boolean $only_main
  25. * @return array
  26. */
  27. public function calculateEntryRepartitionPerFeed($feed = null, $only_main = false) {
  28. $filter = '';
  29. if ($only_main) {
  30. $filter .= 'AND f.priority = 10';
  31. }
  32. if (!is_null($feed)) {
  33. $filter .= "AND e.id_feed = {$feed}";
  34. }
  35. $sql = <<<SQL
  36. SELECT COUNT(1) AS `total`,
  37. COUNT(1) - SUM(e.is_read) AS `unread`,
  38. SUM(e.is_read) AS `read`,
  39. SUM(e.is_favorite) AS `favorite`
  40. FROM {$this->prefix}entry AS e
  41. , {$this->prefix}feed AS f
  42. WHERE e.id_feed = f.id
  43. {$filter}
  44. SQL;
  45. $stm = $this->bd->prepare($sql);
  46. $stm->execute();
  47. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  48. return $res[0];
  49. }
  50. /**
  51. * Calculates entry count per day on a 30 days period.
  52. * Returns the result as a JSON object.
  53. *
  54. * @return JSON object
  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. $sql = <<<SQL
  62. SELECT FLOOR((date - {$midnight}) / 86400) AS day,
  63. COUNT(*) as count
  64. FROM {$this->prefix}entry
  65. WHERE date >= {$oldest} AND date < {$midnight}
  66. GROUP BY day
  67. ORDER BY day ASC
  68. SQL;
  69. $stm = $this->bd->prepare($sql);
  70. $stm->execute();
  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 {$this->prefix}entry AS e
  128. {$restrict}
  129. GROUP BY period
  130. ORDER BY period ASC
  131. SQL;
  132. $stm = $this->bd->prepare($sql);
  133. $stm->execute();
  134. $res = $stm->fetchAll(PDO::FETCH_NAMED);
  135. $repartition = array();
  136. foreach ($res as $value) {
  137. $repartition[(int) $value['period']] = (int) $value['count'];
  138. }
  139. return $repartition;
  140. }
  141. /**
  142. * Calculates the average number of article per hour per feed
  143. *
  144. * @param integer $feed id
  145. * @return integer
  146. */
  147. public function calculateEntryAveragePerFeedPerHour($feed = null) {
  148. return $this->calculateEntryAveragePerFeedPerPeriod(1 / 24, $feed);
  149. }
  150. /**
  151. * Calculates the average number of article per day of week per feed
  152. *
  153. * @param integer $feed id
  154. * @return integer
  155. */
  156. public function calculateEntryAveragePerFeedPerDayOfWeek($feed = null) {
  157. return $this->calculateEntryAveragePerFeedPerPeriod(7, $feed);
  158. }
  159. /**
  160. * Calculates the average number of article per month per feed
  161. *
  162. * @param integer $feed id
  163. * @return integer
  164. */
  165. public function calculateEntryAveragePerFeedPerMonth($feed = null) {
  166. return $this->calculateEntryAveragePerFeedPerPeriod(30, $feed);
  167. }
  168. /**
  169. * Calculates the average number of article per feed
  170. *
  171. * @param float $period number used to divide the number of day in the period
  172. * @param integer $feed id
  173. * @return integer
  174. */
  175. protected function calculateEntryAveragePerFeedPerPeriod($period, $feed = null) {
  176. $restrict = '';
  177. if ($feed) {
  178. $restrict = "WHERE e.id_feed = {$feed}";
  179. }
  180. $sql = <<<SQL
  181. SELECT COUNT(1) AS count
  182. , MIN(date) AS date_min
  183. , MAX(date) AS date_max
  184. FROM {$this->prefix}entry AS e
  185. {$restrict}
  186. SQL;
  187. $stm = $this->bd->prepare($sql);
  188. $stm->execute();
  189. $res = $stm->fetch(PDO::FETCH_NAMED);
  190. $date_min = new \DateTime();
  191. $date_min->setTimestamp($res['date_min']);
  192. $date_max = new \DateTime();
  193. $date_max->setTimestamp($res['date_max']);
  194. $interval = $date_max->diff($date_min, true);
  195. $interval_in_days = $interval->format('%a');
  196. if ($interval_in_days <= 0) {
  197. // Surely only one article.
  198. // We will return count / (period/period) == count.
  199. $interval_in_days = $period;
  200. }
  201. return $res['count'] / ($interval_in_days / $period);
  202. }
  203. /**
  204. * Initialize an array for statistics depending on a range
  205. *
  206. * @param integer $min
  207. * @param integer $max
  208. * @return array
  209. */
  210. protected function initStatsArray($min, $max) {
  211. return array_map(function () {
  212. return 0;
  213. }, array_flip(range($min, $max)));
  214. }
  215. /**
  216. * Calculates feed count per category.
  217. * Returns the result as a JSON object.
  218. *
  219. * @return JSON object
  220. */
  221. public function calculateFeedByCategory() {
  222. $sql = <<<SQL
  223. SELECT c.name AS label
  224. , COUNT(f.id) AS data
  225. FROM {$this->prefix}category AS c,
  226. {$this->prefix}feed AS f
  227. WHERE c.id = f.category
  228. GROUP BY label
  229. ORDER BY data DESC
  230. SQL;
  231. $stm = $this->bd->prepare($sql);
  232. $stm->execute();
  233. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  234. return $res;
  235. }
  236. /**
  237. * Calculates entry count per category.
  238. * Returns the result as a JSON string.
  239. *
  240. * @return JSON object
  241. */
  242. public function calculateEntryByCategory() {
  243. $sql = <<<SQL
  244. SELECT c.name AS label
  245. , COUNT(e.id) AS data
  246. FROM {$this->prefix}category AS c,
  247. {$this->prefix}feed AS f,
  248. {$this->prefix}entry AS e
  249. WHERE c.id = f.category
  250. AND f.id = e.id_feed
  251. GROUP BY label
  252. ORDER BY data DESC
  253. SQL;
  254. $stm = $this->bd->prepare($sql);
  255. $stm->execute();
  256. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  257. return $res;
  258. }
  259. /**
  260. * Calculates the 10 top feeds based on their number of entries
  261. *
  262. * @return array
  263. */
  264. public function calculateTopFeed() {
  265. $sql = <<<SQL
  266. SELECT f.id AS id
  267. , MAX(f.name) AS name
  268. , MAX(c.name) AS category
  269. , COUNT(e.id) AS count
  270. FROM {$this->prefix}category AS c,
  271. {$this->prefix}feed AS f,
  272. {$this->prefix}entry AS e
  273. WHERE c.id = f.category
  274. AND f.id = e.id_feed
  275. GROUP BY f.id
  276. ORDER BY count DESC
  277. LIMIT 10
  278. SQL;
  279. $stm = $this->bd->prepare($sql);
  280. $stm->execute();
  281. return $stm->fetchAll(PDO::FETCH_ASSOC);
  282. }
  283. /**
  284. * Calculates the last publication date for each feed
  285. *
  286. * @return array
  287. */
  288. public function calculateFeedLastDate() {
  289. $sql = <<<SQL
  290. SELECT MAX(f.id) as id
  291. , MAX(f.name) AS name
  292. , MAX(date) AS last_date
  293. , COUNT(*) AS nb_articles
  294. FROM {$this->prefix}feed AS f,
  295. {$this->prefix}entry AS e
  296. WHERE f.id = e.id_feed
  297. GROUP BY f.id
  298. ORDER BY name
  299. SQL;
  300. $stm = $this->bd->prepare($sql);
  301. $stm->execute();
  302. return $stm->fetchAll(PDO::FETCH_ASSOC);
  303. }
  304. /**
  305. * Gets days ready for graphs
  306. *
  307. * @return string
  308. */
  309. public function getDays() {
  310. return $this->convertToTranslatedJson(array(
  311. 'sun',
  312. 'mon',
  313. 'tue',
  314. 'wed',
  315. 'thu',
  316. 'fri',
  317. 'sat',
  318. ));
  319. }
  320. /**
  321. * Gets months ready for graphs
  322. *
  323. * @return string
  324. */
  325. public function getMonths() {
  326. return $this->convertToTranslatedJson(array(
  327. 'jan',
  328. 'feb',
  329. 'mar',
  330. 'apr',
  331. 'may',
  332. 'jun',
  333. 'jul',
  334. 'aug',
  335. 'sep',
  336. 'oct',
  337. 'nov',
  338. 'dec',
  339. ));
  340. }
  341. /**
  342. * Translates array content
  343. *
  344. * @param array $data
  345. * @return JSON object
  346. */
  347. private function convertToTranslatedJson($data = array()) {
  348. $translated = array_map(function($a) {
  349. return _t('gen.date.' . $a);
  350. }, $data);
  351. return $translated;
  352. }
  353. }