Commit 593f2c73 authored by Nacim Goura's avatar Nacim Goura

add statistique

parent 40d432f3
...@@ -20,7 +20,8 @@ caching-compiler@1.1.9 ...@@ -20,7 +20,8 @@ caching-compiler@1.1.9
caching-html-compiler@1.1.2 caching-html-compiler@1.1.2
callback-hook@1.0.10 callback-hook@1.0.10
check@1.2.5 check@1.2.5
coffeescript@1.12.6_1 coffeescript@1.12.7_1
coffeescript-compiler@1.12.7_1
cosmos:browserify@0.10.0 cosmos:browserify@0.10.0
dburles:collection-helpers@1.1.0 dburles:collection-helpers@1.1.0
ddp@1.3.0 ddp@1.3.0
...@@ -57,14 +58,14 @@ livedata@1.0.18 ...@@ -57,14 +58,14 @@ livedata@1.0.18
localstorage@1.1.1 localstorage@1.1.1
logging@1.1.17 logging@1.1.17
matb33:collection-hooks@0.8.4 matb33:collection-hooks@0.8.4
meteor@1.7.0 meteor@1.7.1
meteor-base@1.1.0 meteor-base@1.1.0
minifier-css@1.2.16 minifier-css@1.2.16
minifier-js@2.1.1 minifier-js@2.1.1
minimongo@1.2.1 minimongo@1.2.1
mobile-experience@1.0.4 mobile-experience@1.0.4
mobile-status-bar@1.0.14 mobile-status-bar@1.0.14
modules@0.9.2 modules@0.9.4
modules-runtime@0.8.0 modules-runtime@0.8.0
momentjs:moment@2.18.1 momentjs:moment@2.18.1
mongo@1.1.22 mongo@1.1.22
...@@ -74,7 +75,7 @@ npm-mongo@2.2.30 ...@@ -74,7 +75,7 @@ npm-mongo@2.2.30
observe-sequence@1.0.16 observe-sequence@1.0.16
ordered-dict@1.0.9 ordered-dict@1.0.9
ostrio:cookies@2.2.2 ostrio:cookies@2.2.2
ostrio:files@1.8.0 ostrio:files@1.8.2
percolate:synced-cron@1.3.2 percolate:synced-cron@1.3.2
practicalmeteor:chai@2.1.0_1 practicalmeteor:chai@2.1.0_1
practicalmeteor:loglevel@1.2.0_2 practicalmeteor:loglevel@1.2.0_2
...@@ -94,7 +95,7 @@ service-configuration@1.0.11 ...@@ -94,7 +95,7 @@ service-configuration@1.0.11
session@1.1.7 session@1.1.7
sha@1.0.9 sha@1.0.9
spacebars@1.0.15 spacebars@1.0.15
spacebars-compiler@1.1.2 spacebars-compiler@1.1.3
srp@1.0.10 srp@1.0.10
standard-minifier-css@1.3.4 standard-minifier-css@1.3.4
standard-minifier-js@2.1.1 standard-minifier-js@2.1.1
......
...@@ -3,7 +3,7 @@ import SimpleSchema from 'simpl-schema'; ...@@ -3,7 +3,7 @@ import SimpleSchema from 'simpl-schema';
import { Mongo } from 'meteor/mongo'; import { Mongo } from 'meteor/mongo';
/** /**
* this collection keep tracks of all notification of this application * this collection keep tracks of all jobs of this application
* @type {Mongo.Collection} * @type {Mongo.Collection}
*/ */
const jobsCollection = new Mongo.Collection('jobs'); const jobsCollection = new Mongo.Collection('jobs');
......
...@@ -4,6 +4,7 @@ import { Meteor } from 'meteor/meteor'; ...@@ -4,6 +4,7 @@ import { Meteor } from 'meteor/meteor';
import _ from 'lodash'; import _ from 'lodash';
import Search from '/imports/api/search/server/search'; import Search from '/imports/api/search/server/search';
import formSearchSchema from '/imports/api/search/formSearchSchema'; import formSearchSchema from '/imports/api/search/formSearchSchema';
import statCollection from '/imports/api/statistique/statCollection';
export async function searchWebsite(data, userId) { export async function searchWebsite(data, userId) {
check(data, Object); check(data, Object);
...@@ -11,8 +12,16 @@ export async function searchWebsite(data, userId) { ...@@ -11,8 +12,16 @@ export async function searchWebsite(data, userId) {
const search = new Search(userId); const search = new Search(userId);
try { try {
const results = await search.searchWebsite(data.searchTerm); const results = await search.searchWebsite(data.searchTerm);
const responseTime = results.took ? results.took / 1000 : null;
statCollection.rawCollection().insert({
term: data.searchTerm,
numberResult: results.hits.total,
responseTime,
userId,
createdAt: new Date(),
});
return { return {
time: results.took ? results.took / 1000 : null, time: responseTime,
total: results.hits.total, total: results.hits.total,
list: _.map(results.hits.hits, '_source'), list: _.map(results.hits.hits, '_source'),
}; };
......
import { Meteor } from 'meteor/meteor';
import statCollection from '../statCollection';
Meteor.publish('stats', () => statCollection.find({ userId: Meteor.userId() }));
import SimpleSchema from 'simpl-schema';
import { Mongo } from 'meteor/mongo';
/**
* this collection keep tracks of all stats of this application
* @type {Mongo.Collection}
*/
const statsCollection = new Mongo.Collection('stats');
SimpleSchema.statsCollection = new SimpleSchema({
term: {
type: String,
},
numberResult: {
type: Number,
defaultValue: 0,
},
responseTime: {
type: Number,
},
createdAt: {
type: Date,
},
userId: {
type: String,
},
});
statsCollection.attachSchema(SimpleSchema.statsCollection);
export default statsCollection;
...@@ -39,22 +39,6 @@ adminSection.route('/account', { ...@@ -39,22 +39,6 @@ adminSection.route('/account', {
}], }],
}); });
adminSection.route('/stat', {
name: 'statistique',
action() {
BlazeLayout.render('mainLayoutTpl', {
sidebar: 'sidebarLayoutTpl',
main: 'statTpl',
navbar: 'navbarLayoutTpl',
});
},
triggersEnter: [(context, redirect) => {
if (Meteor.user() && Meteor.user().roles && Meteor.user().roles[0] !== 'admin') {
redirect('home');
}
}],
});
adminSection.route('/notif', { adminSection.route('/notif', {
name: 'notif', name: 'notif',
action() { action() {
......
...@@ -16,7 +16,7 @@ import '/imports/ui/pages/login/login'; ...@@ -16,7 +16,7 @@ import '/imports/ui/pages/login/login';
import '/imports/ui/pages/not-found/not-found'; import '/imports/ui/pages/not-found/not-found';
import '/imports/ui/pages/admin/indexation/indexation'; import '/imports/ui/pages/admin/indexation/indexation';
import '/imports/ui/pages/admin/account/account'; import '/imports/ui/pages/admin/account/account';
import '/imports/ui/pages/admin/statistique/statistique'; import '/imports/ui/pages/statistique/statistique';
import '/imports/ui/pages/notif/notif'; import '/imports/ui/pages/notif/notif';
import '/imports/ui/components/testSearch/testSearch'; import '/imports/ui/components/testSearch/testSearch';
...@@ -58,6 +58,20 @@ FlowRouter.route('/login', { ...@@ -58,6 +58,20 @@ FlowRouter.route('/login', {
}, },
}); });
FlowRouter.route('/stat', {
name: 'statistique',
action() {
BlazeLayout.render('mainLayoutTpl', {
sidebar: 'sidebarLayoutTpl',
main: 'statTpl',
navbar: 'navbarLayoutTpl',
});
},
subscriptions() {
this.register('stats', Meteor.subscribe('stats'));
},
});
FlowRouter.route('/logout', { FlowRouter.route('/logout', {
name: 'logout', name: 'logout',
action() { action() {
......
...@@ -9,8 +9,9 @@ import '/imports/api/testSearch/methods'; ...@@ -9,8 +9,9 @@ import '/imports/api/testSearch/methods';
import '/imports/api/search/methods'; import '/imports/api/search/methods';
// publications // publications
import '/imports/api/crawl/publications'; import '/imports/api/crawl/server/publications';
import '/imports/api/config/publications'; import '/imports/api/config/server/publications';
import '/imports/api/statistique/server/publications';
// tabular // tabular
import '/imports/tabular/tabularUser'; import '/imports/tabular/tabularUser';
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
<a href="/admin/account"><i class="fa fa-fw fa-user fa-20px" aria-hidden="true"></i> Comptes</a> <a href="/admin/account"><i class="fa fa-fw fa-user fa-20px" aria-hidden="true"></i> Comptes</a>
</li> </li>
<li> <li>
<a href="/admin/stat"><i class="fa fa-fw fa-bar-chart-o fa-20px" aria-hidden="true"></i> Statistiques</a> <a href="/stat"><i class="fa fa-fw fa-bar-chart-o fa-20px" aria-hidden="true"></i> Statistiques</a>
</li> </li>
{{/if}} {{/if}}
<li> <li>
......
<template name="statTpl">
<div class="row">
<h3 class="text-center">Page des statistiques</h3>
<div class="panel panel-default">
<div class="panel-body">
<div class="row">
<select name="selectDateChart" id="selectDateChart" class="form-control">
<option value="day">Jour</option>
<option value="week">Semaine</option>
<option value="month">Mois</option>
</select>
<div class="col-md-6">
<canvas id="termFrequencyChart"></canvas>
</div>
<div class="col-md-6">
<canvas id="responseTimeChart"></canvas>
</div>
</div>
</div>
</div>
</div>
</template>
import _ from 'lodash';
import { Template } from 'meteor/templating';
import moment from 'moment';
import statCollection from '/imports/api/statistique/statCollection';
import Chart from 'chart.js';
import './statistique.html';
function countOccurrence(arr) {
const a = [];
const b = [];
let prev = null;
arr.sort();
for (let i = 0; i < arr.length; i++) {
if (arr[i] !== prev) {
a.push(arr[i]);
b.push(1);
} else {
b[b.length - 1]++;
}
prev = arr[i];
}
return [a, b];
}
function defineFrequencyTerm(stats) {
const listTerm = _.map(stats, 'term');
const data = countOccurrence(listTerm);
const ctx = document.getElementById('termFrequencyChart').getContext('2d');
new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: data[0],
datasets: [{
backgroundColor: ['#3e95cd', '#8e5ea2', '#3cba9f', '#e8c3b9', '#c45850'],
data: data[1],
}],
},
options: {
legend: { display: false },
title: {
display: true,
text: 'Fréquence des termes',
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
callback(value) { if (value % 1 === 0) { return value; } },
},
}],
},
},
});
}
function defineResponseTime(stats, filter) {
const format = filter === 'day' ? 'LT' : 'L';
const ctx = document.getElementById('responseTimeChart').getContext('2d');
const data = {};
_.forEach(stats, (item) => {
item.createdAt = moment(item.createdAt).format(format);
if (data[item.createdAt]) {
data[item.createdAt].number++;
data[item.createdAt].responseTime += item.responseTime;
} else {
data[item.createdAt] = {
number: 1,
responseTime: item.responseTime,
};
}
});
_.forOwn(data, (item) => {
item.responseTime /= item.number;
});
new Chart(ctx, {
type: 'line',
data: {
labels: _.keys(data),
datasets: [{
data: _.map(data, 'responseTime'),
borderColor: '#3e95cd',
fill: false,
}],
},
options: {
legend: { display: false },
title: {
display: true,
text: 'Temps de réponse (en seconde)',
},
scales: {
yAxes: [{
stacked: true,
}],
},
},
});
}
Template.statTpl.hooks({
rendered() {
this.autorun(() => {
const stats = statCollection.find({}).fetch();
if (stats && stats.length) {
defineResponseTime(stats, 'day');
defineFrequencyTerm(stats);
}
});
},
});
Template.statTpl.events({
'change #selectDateChart': (event) => {
const filter = event.currentTarget.value;
if (filter) {
const stats = statCollection.find({
createdAt: {
$gte: moment().startOf(filter).toDate(),
$lte: moment().endOf(filter).toDate(),
},
}).fetch();
if (stats && stats.length) {
defineResponseTime(stats, filter);
defineFrequencyTerm(stats);
}
}
},
});
...@@ -9,35 +9,36 @@ ...@@ -9,35 +9,36 @@
}, },
"dependencies": { "dependencies": {
"awesomplete": "^1.1.2", "awesomplete": "^1.1.2",
"babel-runtime": "^6.23.0", "babel-runtime": "^6.26.0",
"bcrypt": "^1.0.2", "bcrypt": "^1.0.2",
"bootstrap-sass": "^3.3.7", "bootstrap-sass": "^3.3.7",
"chart.js": "^2.6.0",
"crawler": "^1.0.5", "crawler": "^1.0.5",
"datatables.net-bs": "^1.10.15", "datatables.net-bs": "^1.10.15",
"detergent": "^2.30.0", "detergent": "^2.30.1",
"elasticsearch": "^13.2.0", "elasticsearch": "^13.3.1",
"izitoast": "^1.1.4", "izitoast": "^1.1.5",
"jquery": "^1.11.2", "jquery": "^1.11.2",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"meteor-node-stubs": "~0.2.11", "meteor-node-stubs": "~0.2.11",
"moment": "^2.18.1", "moment": "^2.18.1",
"sanitize-html": "^1.14.1", "sanitize-html": "^1.14.1",
"simpl-schema": "^0.3.1", "simpl-schema": "^0.3.2",
"sitemapper": "^2.1.13", "sitemapper": "^2.1.13",
"string-unfancy": "^1.0.9", "string-unfancy": "^2.0.1",
"sweetalert2": "^6.6.6", "sweetalert2": "^6.6.9",
"twitter": "^1.7.1" "twitter": "^1.7.1"
}, },
"devDependencies": { "devDependencies": {
"@meteorjs/eslint-config-meteor": "^1.0.5", "@meteorjs/eslint-config-meteor": "^1.0.5",
"babel-eslint": "^7.2.3", "babel-eslint": "^7.2.3",
"eslint": "^3.19.0", "eslint": "^4.5.0",
"eslint-config-airbnb": "^15.0.2", "eslint-config-airbnb": "^15.1.0",
"eslint-import-resolver-meteor": "^0.4.0", "eslint-import-resolver-meteor": "^0.4.0",
"eslint-plugin-import": "^2.7.0", "eslint-plugin-import": "^2.7.0",
"eslint-plugin-jsx-a11y": "^5.1.1", "eslint-plugin-jsx-a11y": "^5.1.1",
"eslint-plugin-meteor": "^4.0.1", "eslint-plugin-meteor": "^4.1.4",
"eslint-plugin-promise": "^3.5.0", "eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^7.1.0" "eslint-plugin-react": "^7.1.2"
} }
} }
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment