summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGravatar Alexis Degrugillier <github@ainw.org> 2014-07-24 21:57:59 -0400
committerGravatar Alexis Degrugillier <github@ainw.org> 2014-07-24 21:57:59 -0400
commitd049c1bc806dc0677a4b2b17faf06080600c372f (patch)
treefa33bdca9b49e1cd3cc74de68a983f9a14b32272 /app
parentd5d3f6dcfa21e7defb26f3d470289e957bdb3dde (diff)
Add article repartition in stats
Add article repartition per hour, per day of week, per month for all feeds but also for individual feeds.
Diffstat (limited to 'app')
-rw-r--r--app/Controllers/statsController.php15
-rw-r--r--app/Models/StatsDAO.php129
-rw-r--r--app/i18n/en.php47
-rw-r--r--app/i18n/fr.php23
-rw-r--r--app/layout/aside_flux.phtml1
-rw-r--r--app/layout/aside_stats.phtml3
-rw-r--r--app/views/stats/main.phtml127
-rw-r--r--app/views/stats/repartition.phtml100
8 files changed, 304 insertions, 141 deletions
diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php
index 45d13e043..06a20c2a6 100644
--- a/app/Controllers/statsController.php
+++ b/app/Controllers/statsController.php
@@ -55,7 +55,20 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
$this->view->idleFeeds = $idleFeeds;
}
-
+
+ public function repartitionAction() {
+ $statsDAO = FreshRSS_Factory::createStatsDAO();
+ $feedDAO = FreshRSS_Factory::createFeedDao();
+ Minz_View::appendScript(Minz_Url::display('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js')));
+ $id = Minz_Request::param ('id', null);
+ $this->view->feed = $feedDAO->searchById($id);
+ $this->view->days = $statsDAO->getDays();
+ $this->view->months = $statsDAO->getMonths();
+ $this->view->repartitionHour = $statsDAO->calculateEntryRepartitionPerFeedPerHour($id);
+ $this->view->repartitionDayOfWeek = $statsDAO->calculateEntryRepartitionPerFeedPerDayOfWeek($id);
+ $this->view->repartitionMonth = $statsDAO->calculateEntryRepartitionPerFeedPerMonth($id);
+ }
+
public function firstAction() {
if (!$this->view->loginOk) {
Minz_Error::error(
diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php
index 9a88a4fcf..ee8d0d663 100644
--- a/app/Models/StatsDAO.php
+++ b/app/Models/StatsDAO.php
@@ -85,9 +85,83 @@ SQL;
* @return array
*/
protected function initEntryCountArray() {
+ return $this->initStatsArray(-self::ENTRY_COUNT_PERIOD, -1);
+ }
+
+ /**
+ * Calculates the number of article per hour of the day per feed
+ *
+ * @param integer $feed id
+ * @return string
+ */
+ public function calculateEntryRepartitionPerFeedPerHour($feed = null) {
+ return $this->calculateEntryRepartitionPerFeedPerPeriod('%k', $feed);
+ }
+
+ /**
+ * Calculates the number of article per day of week per feed
+ *
+ * @param integer $feed id
+ * @return string
+ */
+ public function calculateEntryRepartitionPerFeedPerDayOfWeek($feed = null) {
+ return $this->calculateEntryRepartitionPerFeedPerPeriod('%w', $feed);
+ }
+
+ /**
+ * Calculates the number of article per month per feed
+ *
+ * @param integer $feed
+ * @return string
+ */
+ public function calculateEntryRepartitionPerFeedPerMonth($feed = null) {
+ return $this->calculateEntryRepartitionPerFeedPerPeriod('%m', $feed);
+ }
+
+ /**
+ * Calculates the number of article per period per feed
+ *
+ * @param string $period format string to use for grouping
+ * @param integer $feed id
+ * @return string
+ */
+ protected function calculateEntryRepartitionPerFeedPerPeriod($period, $feed = null) {
+ if ($feed) {
+ $restrict = "WHERE e.id_feed = {$feed}";
+ } else {
+ $restrict = '';
+ }
+ $sql = <<<SQL
+SELECT DATE_FORMAT(FROM_UNIXTIME(e.date), '{$period}') AS period
+, COUNT(1) AS count
+FROM {$this->prefix}entry AS e
+{$restrict}
+GROUP BY period
+ORDER BY period ASC
+SQL;
+
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ $res = $stm->fetchAll(PDO::FETCH_NAMED);
+
+ foreach ($res as $value) {
+ $repartition[(int) $value['period']] = (int) $value['count'];
+ }
+
+ return $this->convertToSerie($repartition);
+ }
+
+ /**
+ * Initialize an array for statistics depending on a range
+ *
+ * @param integer $min
+ * @param integer $max
+ * @return array
+ */
+ protected function initStatsArray($min, $max) {
return array_map(function () {
return 0;
- }, array_flip(range(-self::ENTRY_COUNT_PERIOD, -1)));
+ }, array_flip(range($min, $max)));
}
/**
@@ -205,4 +279,57 @@ SQL;
return json_encode($serie);
}
+ /**
+ * Gets days ready for graphs
+ *
+ * @return string
+ */
+ public function getDays() {
+ return $this->convertToTranslatedJson(array(
+ 'sun',
+ 'mon',
+ 'tue',
+ 'wed',
+ 'thu',
+ 'fri',
+ 'sat',
+ ));
+ }
+
+ /**
+ * Gets months ready for graphs
+ *
+ * @return string
+ */
+ public function getMonths() {
+ return $this->convertToTranslatedJson(array(
+ 'jan',
+ 'feb',
+ 'mar',
+ 'apr',
+ 'may',
+ 'jun',
+ 'jul',
+ 'aug',
+ 'sep',
+ 'oct',
+ 'nov',
+ 'dec',
+ ));
+ }
+
+ /**
+ * Translates array content and encode it as JSON
+ *
+ * @param array $data
+ * @return string
+ */
+ private function convertToTranslatedJson($data = array()) {
+ $translated = array_map(function ($a) {
+ return Minz_Translate::t($a);
+ }, $data);
+
+ return json_encode($translated);
+ }
+
}
diff --git a/app/i18n/en.php b/app/i18n/en.php
index 8634f99b5..10327c7f5 100644
--- a/app/i18n/en.php
+++ b/app/i18n/en.php
@@ -48,6 +48,10 @@ return array (
'stats' => 'Statistics',
'stats_idle' => 'Idle feeds',
'stats_main' => 'Main statistics',
+ 'stats_repartition' => 'Articles repartition',
+ 'stats_entry_per_hour' => 'Per hour',
+ 'stats_entry_per_day_of_week' => 'Per day of week',
+ 'stats_entry_per_month' => 'Per month',
'last_week' => 'Last week',
'last_month' => 'Last month',
@@ -341,18 +345,37 @@ return array (
'confirm_action' => 'Are you sure you want to perform this action? It cannot be cancelled!',
// DATE
- 'january' => 'january',
- 'february' => 'february',
- 'march' => 'march',
- 'april' => 'april',
- 'may' => 'may',
- 'june' => 'june',
- 'july' => 'july',
- 'august' => 'august',
- 'september' => 'september',
- 'october' => 'october',
- 'november' => 'november',
- 'december' => 'december',
+ 'january' => 'January',
+ 'february' => 'February',
+ 'march' => 'March',
+ 'april' => 'April',
+ 'may' => 'May',
+ 'june' => 'June',
+ 'july' => 'July',
+ 'august' => 'August',
+ 'september' => 'September',
+ 'october' => 'October',
+ 'november' => 'November',
+ 'december' => 'December',
+ 'january' => 'Jan',
+ 'february' => 'Feb',
+ 'march' => 'Mar',
+ 'april' => 'Apr',
+ 'may' => 'May',
+ 'june' => 'Jun',
+ 'july' => 'Jul',
+ 'august' => 'Aug',
+ 'september' => 'Sep',
+ 'october' => 'Oct',
+ 'november' => 'Nov',
+ 'december' => 'Dec',
+ 'sun' => 'Sun',
+ 'mon' => 'Mon',
+ 'tue' => 'Tue',
+ 'wed' => 'Wed',
+ 'thu' => 'Thu',
+ 'fri' => 'Fri',
+ 'sat' => 'Sat',
// special format for date() function
'Jan' => '\J\a\n\u\a\r\y',
'Feb' => '\F\e\b\r\u\a\r\y',
diff --git a/app/i18n/fr.php b/app/i18n/fr.php
index e04078dba..6ab3d7335 100644
--- a/app/i18n/fr.php
+++ b/app/i18n/fr.php
@@ -48,6 +48,10 @@ return array (
'stats' => 'Statistiques',
'stats_idle' => 'Flux inactifs',
'stats_main' => 'Statistiques principales',
+ 'stats_repartition' => 'Répartition des articles',
+ 'stats_entry_per_hour' => 'Par heure',
+ 'stats_entry_per_day_of_week' => 'Par jour de la semaine',
+ 'stats_entry_per_month' => 'Par mois',
'last_week' => 'La dernière semaine',
'last_month' => 'Le dernier mois',
@@ -353,6 +357,25 @@ return array (
'october' => 'octobre',
'november' => 'novembre',
'december' => 'décembre',
+ 'jan' => 'jan.',
+ 'feb' => 'fév.',
+ 'mar' => 'mar.',
+ 'apr' => 'avr.',
+ 'may' => 'mai.',
+ 'jun' => 'juin',
+ 'jul' => 'jui.',
+ 'aug' => 'août',
+ 'sep' => 'sep.',
+ 'oct' => 'oct.',
+ 'nov' => 'nov.',
+ 'dec' => 'déc.',
+ 'sun' => 'dim.',
+ 'mon' => 'lun.',
+ 'tue' => 'mar.',
+ 'wed' => 'mer.',
+ 'thu' => 'jeu.',
+ 'fri' => 'ven.',
+ 'sat' => 'sam.',
// format spécial pour la fonction date()
'Jan' => '\j\a\n\v\i\e\r',
'Feb' => '\f\é\v\r\i\e\r',
diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml
index 817dae676..5fbb36730 100644
--- a/app/layout/aside_flux.phtml
+++ b/app/layout/aside_flux.phtml
@@ -83,6 +83,7 @@
<li class="item"><a href="<?php echo _url ('configure', 'feed', 'id', '!!!!!!'); ?>"><?php echo Minz_Translate::t ('administration'); ?></a></li>
<li class="item"><a href="<?php echo _url ('feed', 'actualize', 'id', '!!!!!!'); ?>"><?php echo Minz_Translate::t ('actualize'); ?></a></li>
<li class="item"><a href="<?php echo _url ('entry', 'read', 'get', 'f_!!!!!!'); ?>"><?php echo Minz_Translate::t ('mark_read'); ?></a></li>
+ <li class="item"><a href="<?php echo _url ('stats', 'repartition', 'id', '!!!!!!'); ?>"><?php echo Minz_Translate::t ('stats'); ?></a></li>
<?php } ?>
</ul>
</script>
diff --git a/app/layout/aside_stats.phtml b/app/layout/aside_stats.phtml
index 32a3f5dee..fbfb9d84d 100644
--- a/app/layout/aside_stats.phtml
+++ b/app/layout/aside_stats.phtml
@@ -6,4 +6,7 @@
<li class="item<?php echo Minz_Request::actionName () == 'idle' ? ' active' : ''; ?>">
<a href="<?php echo _url ('stats', 'idle'); ?>"><?php echo Minz_Translate::t ('stats_idle'); ?></a>
</li>
+ <li class="item<?php echo Minz_Request::actionName () == 'repartition' ? ' active' : ''; ?>">
+ <a href="<?php echo _url ('stats', 'repartition'); ?>"><?php echo Minz_Translate::t ('stats_repartition'); ?></a>
+ </li>
</ul>
diff --git a/app/views/stats/main.phtml b/app/views/stats/main.phtml
deleted file mode 100644
index fe372e221..000000000
--- a/app/views/stats/main.phtml
+++ /dev/null
@@ -1,127 +0,0 @@
-<?php $this->partial('aside_stats'); ?>
-
-<div class="post content">
- <a href="<?php echo _url ('index', 'index'); ?>"><?php echo Minz_Translate::t ('back_to_rss_feeds'); ?></a>
-
- <h1><?php echo Minz_Translate::t ('stats_main'); ?></h1>
-
- <div class="stat">
- <h2><?php echo Minz_Translate::t ('stats_entry_repartition'); ?></h2>
- <table>
- <thead>
- <tr>
- <th> </th>
- <th><?php echo Minz_Translate::t ('main_stream'); ?></th>
- <th><?php echo Minz_Translate::t ('all_feeds'); ?></th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <th><?php echo Minz_Translate::t ('status_total'); ?></th>
- <td class="numeric"><?php echo formatNumber($this->repartition['main_stream']['total']); ?></td>
- <td class="numeric"><?php echo formatNumber($this->repartition['all_feeds']['total']); ?></td>
- </tr>
- <tr>
- <th><?php echo Minz_Translate::t ('status_read'); ?></th>
- <td class="numeric"><?php echo formatNumber($this->repartition['main_stream']['read']); ?></td>
- <td class="numeric"><?php echo formatNumber($this->repartition['all_feeds']['read']); ?></td>
- </tr>
- <tr>
- <th><?php echo Minz_Translate::t ('status_unread'); ?></th>
- <td class="numeric"><?php echo formatNumber($this->repartition['main_stream']['unread']); ?></td>
- <td class="numeric"><?php echo formatNumber($this->repartition['all_feeds']['unread']); ?></td>
- </tr>
- <tr>
- <th><?php echo Minz_Translate::t ('status_favorites'); ?></th>
- <td class="numeric"><?php echo formatNumber($this->repartition['main_stream']['favorite']); ?></td>
- <td class="numeric"><?php echo formatNumber($this->repartition['all_feeds']['favorite']); ?></td>
- </tr>
- </tbody>
- </table>
- </div>
-
- <div class="stat">
- <h2><?php echo Minz_Translate::t ('stats_entry_per_day'); ?></h2>
- <div id="statsEntryPerDay" style="height: 300px"></div>
- </div>
-
- <div class="stat">
- <h2><?php echo Minz_Translate::t ('stats_feed_per_category'); ?></h2>
- <div id="statsFeedPerCategory" style="height: 300px"></div>
- <div id="statsFeedPerCategoryLegend"></div>
- </div>
-
- <div class="stat">
- <h2><?php echo Minz_Translate::t ('stats_entry_per_category'); ?></h2>
- <div id="statsEntryPerCategory" style="height: 300px"></div>
- <div id="statsEntryPerCategoryLegend"></div>
- </div>
-
- <div class="stat">
- <h2><?php echo Minz_Translate::t ('stats_top_feed'); ?></h2>
- <table>
- <thead>
- <tr>
- <th><?php echo Minz_Translate::t ('feed'); ?></th>
- <th><?php echo Minz_Translate::t ('category'); ?></th>
- <th><?php echo Minz_Translate::t ('stats_entry_count'); ?></th>
- </tr>
- </thead>
- <tbody>
- <?php foreach ($this->topFeed as $feed): ?>
- <tr>
- <td><?php echo $feed['name']; ?></td>
- <td><?php echo $feed['category']; ?></td>
- <td class="numeric"><?php echo formatNumber($feed['count']); ?></td>
- </tr>
- <?php endforeach;?>
- </tbody>
- </table>
- </div>
-</div>
-
-<script>
-"use strict";
-function initStats() {
- if (!window.Flotr) {
- if (window.console) {
- console.log('FreshRSS waiting for Flotr…');
- }
- window.setTimeout(initStats, 50);
- return;
- }
- // Entry per day
- Flotr.draw(document.getElementById('statsEntryPerDay'),
- [<?php echo $this->count ?>],
- {
- grid: {verticalLines: false},
- bars: {horizontal: false, show: true},
- xaxis: {noTicks: 6, showLabels: false, tickDecimals: 0},
- yaxis: {min: 0},
- mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
- });
- // Feed per category
- Flotr.draw(document.getElementById('statsFeedPerCategory'),
- <?php echo $this->feedByCategory ?>,
- {
- grid: {verticalLines: false, horizontalLines: false},
- pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
- xaxis: {showLabels: false},
- yaxis: {showLabels: false},
- mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
- legend: {container: document.getElementById('statsFeedPerCategoryLegend'), noColumns: 3}
- });
- // Entry per category
- Flotr.draw(document.getElementById('statsEntryPerCategory'),
- <?php echo $this->entryByCategory ?>,
- {
- grid: {verticalLines: false, horizontalLines: false},
- pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
- xaxis: {showLabels: false},
- yaxis: {showLabels: false},
- mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
- legend: {container: document.getElementById('statsEntryPerCategoryLegend'), noColumns: 3}
- });
-}
-initStats();
-</script>
diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml
new file mode 100644
index 000000000..4455abe2a
--- /dev/null
+++ b/app/views/stats/repartition.phtml
@@ -0,0 +1,100 @@
+<?php $this->partial('aside_stats'); ?>
+
+<div class="post content">
+ <a href="<?php echo _url ('index', 'index'); ?>"><?php echo _t ('back_to_rss_feeds'); ?></a>
+
+ <?php if ($this->feed) {?>
+ <h1>
+ <?php echo _t ('stats_repartition'), " - "; ?>
+ <a href="<?php echo _url('configure', 'feed', 'id', $this->feed->id()); ?>"
+ title="<?php echo date('Y-m-d', $feed['last_date']); ?>">
+ <?php echo $this->feed->name(); ?>
+ </a>
+ </h1>
+ <?php } else {?>
+ <h1><?php echo _t ('stats_repartition'); ?></h1>
+ <?php }?>
+
+ <div class="stat">
+ <h2><?php echo _t ('stats_entry_per_hour'); ?></h2>
+ <div id="statsEntryPerHour" style="height: 300px"></div>
+ </div>
+
+ <div class="stat">
+ <h2><?php echo _t ('stats_entry_per_day_of_week'); ?></h2>
+ <div id="statsEntryPerDayOfWeek" style="height: 300px"></div>
+ </div>
+
+ <div class="stat">
+ <h2><?php echo _t ('stats_entry_per_month'); ?></h2>
+ <div id="statsEntryPerMonth" style="height: 300px"></div>
+ </div>
+</div>
+
+<script>
+"use strict";
+function initStats() {
+ if (!window.Flotr) {
+ if (window.console) {
+ console.log('FreshRSS waiting for Flotr…');
+ }
+ window.setTimeout(initStats, 50);
+ return;
+ }
+ // Entry per hour
+ Flotr.draw(document.getElementById('statsEntryPerHour'),
+ [<?php echo $this->repartitionHour ?>],
+ {
+ grid: {verticalLines: false},
+ bars: {horizontal: false, show: true},
+ xaxis: {noTicks: 23,
+ tickFormatter: function(x) {
+ var x = parseInt(x);
+ return x + 1;
+ },
+ min: -0.9,
+ max: 23.9,
+ tickDecimals: 0},
+ yaxis: {min: 0},
+ mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
+ });
+ // Entry per day of week
+ Flotr.draw(document.getElementById('statsEntryPerDayOfWeek'),
+ [<?php echo $this->repartitionDayOfWeek ?>],
+ {
+ grid: {verticalLines: false},
+ bars: {horizontal: false, show: true},
+ xaxis: {noTicks: 6,
+ tickFormatter: function(x) {
+ var x = parseInt(x),
+ days = <?php echo $this->days?>;
+ return days[x];
+ },
+ min: -0.9,
+ max: 6.9,
+ tickDecimals: 0},
+ yaxis: {min: 0},
+ mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
+ });
+ // Entry per month
+ Flotr.draw(document.getElementById('statsEntryPerMonth'),
+ [<?php echo $this->repartitionMonth ?>],
+ {
+ grid: {verticalLines: false},
+ bars: {horizontal: false, show: true},
+ xaxis: {noTicks: 12,
+ tickFormatter: function(x) {
+ var x = parseInt(x),
+ months = <?php echo $this->months?>;
+ return months[(x - 1)];
+ },
+ min: 0.1,
+ max: 12.9,
+ tickDecimals: 0},
+ yaxis: {min: 0},
+ mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
+ });
+
+}
+initStats();
+</script> \ No newline at end of file