CSP for statistics

https://github.com/FreshRSS/FreshRSS/issues/1075
This commit is contained in:
Alexandre Alapetite
2016-02-21 17:26:37 +01:00
parent e3dc7d46e1
commit 264d05297c
6 changed files with 169 additions and 150 deletions

View File

@@ -55,9 +55,9 @@ SQL;
/**
* Calculates entry count per day on a 30 days period.
* Returns the result as a JSON string.
* Returns the result as a JSON object.
*
* @return string
* @return JSON object
*/
public function calculateEntryCount() {
$count = $this->initEntryCountArray();
@@ -257,9 +257,9 @@ SQL;
/**
* Calculates feed count per category.
* Returns the result as a JSON string.
* Returns the result as a JSON object.
*
* @return string
* @return JSON object
*/
public function calculateFeedByCategory() {
$sql = <<<SQL
@@ -282,7 +282,7 @@ SQL;
* Calculates entry count per category.
* Returns the result as a JSON string.
*
* @return string
* @return JSON object
*/
public function calculateEntryByCategory() {
$sql = <<<SQL
@@ -357,7 +357,7 @@ SQL;
$serie[] = array($key, $value);
}
return json_encode($serie);
return $serie;
}
protected function convertToPieSerie($data) {
@@ -368,7 +368,7 @@ SQL;
$serie[] = $value;
}
return json_encode($serie);
return $serie;
}
/**
@@ -411,17 +411,17 @@ SQL;
}
/**
* Translates array content and encode it as JSON
* Translates array content
*
* @param array $data
* @return string
* @return JSON object
*/
private function convertToTranslatedJson($data = array()) {
$translated = array_map(function($a) {
return _t('gen.date.' . $a);
}, $data);
return json_encode($translated);
return $translated;
}
}

View File

@@ -4,9 +4,9 @@ class FreshRSS_StatsDAOSQLite extends FreshRSS_StatsDAO {
/**
* Calculates entry count per day on a 30 days period.
* Returns the result as a JSON string.
* Returns the result as a JSON object.
*
* @return string
* @return JSON object
*/
public function calculateEntryCount() {
$count = $this->initEntryCountArray();

View File

@@ -82,58 +82,12 @@
</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
var avg = [];
for (var i = -31; i <= 0; i++) {
avg.push([i, <?php echo $this->average?>]);
}
Flotr.draw(document.getElementById('statsEntryPerDay'),
[{
data: <?php echo $this->count ?>,
bars: {horizontal: false, show: true}
},{
data: avg,
lines: {show: true},
label: "<?php echo $this->average?>"
}],
{
grid: {verticalLines: false},
xaxis: {noTicks: 6, showLabels: false, tickDecimals: 0, min: -30.75, max: -0.25},
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>
<script id="jsonStats" type="application/json"><?php
echo htmlspecialchars(json_encode(array(
'average' => $this->average,
'dataCount' => $this->count,
'feedByCategory' => $this->feedByCategory,
'entryByCategory' => $this->entryByCategory,
), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES);
?></script>
<script src="../scripts/stats.js?<?php echo @filemtime(PUBLIC_PATH . '/scripts/stats.js'); ?>"></script>

View File

@@ -30,20 +30,20 @@
<?php }?>
<div class="stat">
<table>
<table>
<tr>
<th><?php echo _t('admin.stats.status_total'); ?></th>
<th><?php echo _t('admin.stats.status_read'); ?></th>
<th><?php echo _t('admin.stats.status_unread'); ?></th>
<th><?php echo _t('admin.stats.status_favorites'); ?></th>
<th><?php echo _t('admin.stats.status_total'); ?></th>
<th><?php echo _t('admin.stats.status_read'); ?></th>
<th><?php echo _t('admin.stats.status_unread'); ?></th>
<th><?php echo _t('admin.stats.status_favorites'); ?></th>
</tr>
<tr>
<td class="numeric"><?php echo $this->repartition['total']; ?></td>
<td class="numeric"><?php echo $this->repartition['read']; ?></td>
<td class="numeric"><?php echo $this->repartition['unread']; ?></td>
<td class="numeric"><?php echo $this->repartition['favorite']; ?></td>
<td class="numeric"><?php echo $this->repartition['total']; ?></td>
<td class="numeric"><?php echo $this->repartition['read']; ?></td>
<td class="numeric"><?php echo $this->repartition['unread']; ?></td>
<td class="numeric"><?php echo $this->repartition['favorite']; ?></td>
</tr>
</table>
</table>
</div>
<div class="stat">
@@ -62,76 +62,13 @@
</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'),
[{
data: <?php echo $this->repartitionHour ?>,
bars: {horizontal: false, show: true}
}],
{
grid: {verticalLines: false},
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'),
[{
data: <?php echo $this->repartitionDayOfWeek ?>,
bars: {horizontal: false, show: true}
}],
{
grid: {verticalLines: false},
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'),
[{
data: <?php echo $this->repartitionMonth ?>,
bars: {horizontal: false, show: true}
}],
{
grid: {verticalLines: false},
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>
<script id="jsonRepartition" type="application/json"><?php
echo htmlspecialchars(json_encode(array(
'repartitionHour' => $this->repartitionHour,
'repartitionDayOfWeek' => $this->repartitionDayOfWeek,
'days' => $this->days,
'repartitionMonth' => $this->repartitionMonth,
'months' => $this->months,
), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES);
?></script>
<script src="../scripts/repartition.js?<?php echo @filemtime(PUBLIC_PATH . '/scripts/repartition.js'); ?>"></script>

72
p/scripts/repartition.js Normal file
View File

@@ -0,0 +1,72 @@
"use strict";
function initStats() {
if (!window.Flotr) {
if (window.console) {
console.log('FreshRSS waiting for Flotr…');
}
window.setTimeout(initStats, 50);
return;
}
var jsonRepartition = document.getElementById('jsonRepartition'),
stats = JSON.parse(jsonRepartition.innerHTML);
jsonRepartition.outerHTML = '';
// Entry per hour
Flotr.draw(document.getElementById('statsEntryPerHour'),
[{
data: stats.repartitionHour,
bars: {horizontal: false, show: true}
}],
{
grid: {verticalLines: false},
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'),
[{
data: stats.repartitionDayOfWeek,
bars: {horizontal: false, show: true}
}],
{
grid: {verticalLines: false},
xaxis: {noTicks: 6,
tickFormatter: function(x) {
var x = parseInt(x);
return stats.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'),
[{
data: stats.repartitionMonth,
bars: {horizontal: false, show: true}
}],
{
grid: {verticalLines: false},
xaxis: {noTicks: 12,
tickFormatter: function(x) {
var x = parseInt(x);
return stats.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();

56
p/scripts/stats.js Normal file
View File

@@ -0,0 +1,56 @@
"use strict";
function initStats() {
if (!window.Flotr) {
if (window.console) {
console.log('FreshRSS waiting for Flotr…');
}
window.setTimeout(initStats, 50);
return;
}
var jsonStats = document.getElementById('jsonStats'),
stats = JSON.parse(jsonStats.innerHTML);
jsonStats.outerHTML = '';
// Entry per day
var avg = [];
for (var i = -31; i <= 0; i++) {
avg.push([i, stats.average]);
}
Flotr.draw(document.getElementById('statsEntryPerDay'),
[{
data: stats.dataCount,
bars: {horizontal: false, show: true}
},{
data: avg,
lines: {show: true},
label: stats.average,
}],
{
grid: {verticalLines: false},
xaxis: {noTicks: 6, showLabels: false, tickDecimals: 0, min: -30.75, max: -0.25},
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'),
stats.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'),
stats.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();