StatsDAO.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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. * The repartition includes:
  7. * - total entries
  8. * - read entries
  9. * - unread entries
  10. * - favorite entries
  11. *
  12. * @return type
  13. */
  14. public function calculateEntryRepartition() {
  15. $repartition = array();
  16. // Generates the repartition for the main stream of entry
  17. $sql = <<<SQL
  18. SELECT COUNT(1) AS `total`,
  19. COUNT(1) - SUM(e.is_read) AS `unread`,
  20. SUM(e.is_read) AS `read`,
  21. SUM(e.is_favorite) AS `favorite`
  22. FROM {$this->prefix}entry AS e
  23. , {$this->prefix}feed AS f
  24. WHERE e.id_feed = f.id
  25. AND f.priority = 10
  26. SQL;
  27. $stm = $this->bd->prepare($sql);
  28. $stm->execute();
  29. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  30. $repartition['main_stream'] = $res[0];
  31. // Generates the repartition for all entries
  32. $sql = <<<SQL
  33. SELECT COUNT(1) AS `total`,
  34. COUNT(1) - SUM(e.is_read) AS `unread`,
  35. SUM(e.is_read) AS `read`,
  36. SUM(e.is_favorite) AS `favorite`
  37. FROM {$this->prefix}entry AS e
  38. SQL;
  39. $stm = $this->bd->prepare($sql);
  40. $stm->execute();
  41. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  42. $repartition['all_feeds'] = $res[0];
  43. return $repartition;
  44. }
  45. /**
  46. * Calculates entry count per day on a 30 days period.
  47. * Returns the result as a JSON string.
  48. *
  49. * @return string
  50. */
  51. public function calculateEntryCount() {
  52. $count = $this->initEntryCountArray();
  53. $period = self::ENTRY_COUNT_PERIOD;
  54. // Get stats per day for the last 30 days
  55. $sql = <<<SQL
  56. SELECT DATEDIFF(FROM_UNIXTIME(e.date), NOW()) AS day,
  57. COUNT(1) AS count
  58. FROM {$this->prefix}entry AS e
  59. WHERE FROM_UNIXTIME(e.date, '%Y%m%d') BETWEEN DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -{$period} DAY), '%Y%m%d') AND DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -1 DAY), '%Y%m%d')
  60. GROUP BY day
  61. ORDER BY day ASC
  62. SQL;
  63. $stm = $this->bd->prepare($sql);
  64. $stm->execute();
  65. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  66. foreach ($res as $value) {
  67. $count[$value['day']] = (int) $value['count'];
  68. }
  69. return $this->convertToSerie($count);
  70. }
  71. /**
  72. * Initialize an array for the entry count.
  73. *
  74. * @return array
  75. */
  76. protected function initEntryCountArray() {
  77. return $this->initStatsArray(-self::ENTRY_COUNT_PERIOD, -1);
  78. }
  79. /**
  80. * Calculates the number of article per hour of the day per feed
  81. *
  82. * @param integer $feed id
  83. * @return string
  84. */
  85. public function calculateEntryRepartitionPerFeedPerHour($feed = null) {
  86. return $this->calculateEntryRepartitionPerFeedPerPeriod('%H', $feed);
  87. }
  88. /**
  89. * Calculates the number of article per day of week per feed
  90. *
  91. * @param integer $feed id
  92. * @return string
  93. */
  94. public function calculateEntryRepartitionPerFeedPerDayOfWeek($feed = null) {
  95. return $this->calculateEntryRepartitionPerFeedPerPeriod('%w', $feed);
  96. }
  97. /**
  98. * Calculates the number of article per month per feed
  99. *
  100. * @param integer $feed
  101. * @return string
  102. */
  103. public function calculateEntryRepartitionPerFeedPerMonth($feed = null) {
  104. return $this->calculateEntryRepartitionPerFeedPerPeriod('%m', $feed);
  105. }
  106. /**
  107. * Calculates the number of article per period per feed
  108. *
  109. * @param string $period format string to use for grouping
  110. * @param integer $feed id
  111. * @return string
  112. */
  113. protected function calculateEntryRepartitionPerFeedPerPeriod($period, $feed = null) {
  114. if ($feed) {
  115. $restrict = "WHERE e.id_feed = {$feed}";
  116. } else {
  117. $restrict = '';
  118. }
  119. $sql = <<<SQL
  120. SELECT DATE_FORMAT(FROM_UNIXTIME(e.date), '{$period}') AS period
  121. , COUNT(1) AS count
  122. FROM {$this->prefix}entry AS e
  123. {$restrict}
  124. GROUP BY period
  125. ORDER BY period ASC
  126. SQL;
  127. $stm = $this->bd->prepare($sql);
  128. $stm->execute();
  129. $res = $stm->fetchAll(PDO::FETCH_NAMED);
  130. foreach ($res as $value) {
  131. $repartition[(int) $value['period']] = (int) $value['count'];
  132. }
  133. return $this->convertToSerie($repartition);
  134. }
  135. /**
  136. * Initialize an array for statistics depending on a range
  137. *
  138. * @param integer $min
  139. * @param integer $max
  140. * @return array
  141. */
  142. protected function initStatsArray($min, $max) {
  143. return array_map(function () {
  144. return 0;
  145. }, array_flip(range($min, $max)));
  146. }
  147. /**
  148. * Calculates feed count per category.
  149. * Returns the result as a JSON string.
  150. *
  151. * @return string
  152. */
  153. public function calculateFeedByCategory() {
  154. $sql = <<<SQL
  155. SELECT c.name AS label
  156. , COUNT(f.id) AS data
  157. FROM {$this->prefix}category AS c,
  158. {$this->prefix}feed AS f
  159. WHERE c.id = f.category
  160. GROUP BY label
  161. ORDER BY data DESC
  162. SQL;
  163. $stm = $this->bd->prepare($sql);
  164. $stm->execute();
  165. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  166. return $this->convertToPieSerie($res);
  167. }
  168. /**
  169. * Calculates entry count per category.
  170. * Returns the result as a JSON string.
  171. *
  172. * @return string
  173. */
  174. public function calculateEntryByCategory() {
  175. $sql = <<<SQL
  176. SELECT c.name AS label
  177. , COUNT(e.id) AS data
  178. FROM {$this->prefix}category AS c,
  179. {$this->prefix}feed AS f,
  180. {$this->prefix}entry AS e
  181. WHERE c.id = f.category
  182. AND f.id = e.id_feed
  183. GROUP BY label
  184. ORDER BY data DESC
  185. SQL;
  186. $stm = $this->bd->prepare($sql);
  187. $stm->execute();
  188. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  189. return $this->convertToPieSerie($res);
  190. }
  191. /**
  192. * Calculates the 10 top feeds based on their number of entries
  193. *
  194. * @return array
  195. */
  196. public function calculateTopFeed() {
  197. $sql = <<<SQL
  198. SELECT f.id AS id
  199. , MAX(f.name) AS name
  200. , MAX(c.name) AS category
  201. , COUNT(e.id) AS count
  202. FROM {$this->prefix}category AS c,
  203. {$this->prefix}feed AS f,
  204. {$this->prefix}entry AS e
  205. WHERE c.id = f.category
  206. AND f.id = e.id_feed
  207. GROUP BY f.id
  208. ORDER BY count DESC
  209. LIMIT 10
  210. SQL;
  211. $stm = $this->bd->prepare($sql);
  212. $stm->execute();
  213. return $stm->fetchAll(PDO::FETCH_ASSOC);
  214. }
  215. /**
  216. * Calculates the last publication date for each feed
  217. *
  218. * @return array
  219. */
  220. public function calculateFeedLastDate() {
  221. $sql = <<<SQL
  222. SELECT MAX(f.id) as id
  223. , MAX(f.name) AS name
  224. , MAX(date) AS last_date
  225. FROM {$this->prefix}feed AS f,
  226. {$this->prefix}entry AS e
  227. WHERE f.id = e.id_feed
  228. GROUP BY f.id
  229. ORDER BY name
  230. SQL;
  231. $stm = $this->bd->prepare($sql);
  232. $stm->execute();
  233. return $stm->fetchAll(PDO::FETCH_ASSOC);
  234. }
  235. protected function convertToSerie($data) {
  236. $serie = array();
  237. foreach ($data as $key => $value) {
  238. $serie[] = array($key, $value);
  239. }
  240. return json_encode($serie);
  241. }
  242. protected function convertToPieSerie($data) {
  243. $serie = array();
  244. foreach ($data as $value) {
  245. $value['data'] = array(array(0, (int) $value['data']));
  246. $serie[] = $value;
  247. }
  248. return json_encode($serie);
  249. }
  250. /**
  251. * Gets days ready for graphs
  252. *
  253. * @return string
  254. */
  255. public function getDays() {
  256. return $this->convertToTranslatedJson(array(
  257. 'sun',
  258. 'mon',
  259. 'tue',
  260. 'wed',
  261. 'thu',
  262. 'fri',
  263. 'sat',
  264. ));
  265. }
  266. /**
  267. * Gets months ready for graphs
  268. *
  269. * @return string
  270. */
  271. public function getMonths() {
  272. return $this->convertToTranslatedJson(array(
  273. 'jan',
  274. 'feb',
  275. 'mar',
  276. 'apr',
  277. 'may',
  278. 'jun',
  279. 'jul',
  280. 'aug',
  281. 'sep',
  282. 'oct',
  283. 'nov',
  284. 'dec',
  285. ));
  286. }
  287. /**
  288. * Translates array content and encode it as JSON
  289. *
  290. * @param array $data
  291. * @return string
  292. */
  293. private function convertToTranslatedJson($data = array()) {
  294. $translated = array_map(function ($a) {
  295. return Minz_Translate::t($a);
  296. }, $data);
  297. return json_encode($translated);
  298. }
  299. }