Skip to content

Commit

Permalink
Add stats generator utility
Browse files Browse the repository at this point in the history
  • Loading branch information
Daandelange committed Oct 18, 2021
1 parent 16b53a1 commit 42135f1
Show file tree
Hide file tree
Showing 11 changed files with 333 additions and 25 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Eventually add `--depth 1` to discard the repo commit history. (saves disk space

*Soon...*

For now, check the comments in `options.php` for directions. It's not recommended to use a custom reporting period yet.
For now, check the comments in `options.php` for directions.

#### Language setup
Multi-language websites are supported. For each page, there's a global counter, with an optional counter for each language.
Expand All @@ -74,6 +74,9 @@ Depending on your local laws, you might need to sit down and define how personal
You might want to inspect the source code to know what's going on in details.
As the license states, there's no guarantee whatsoever.

#### Stats Generator
If you'd like to populate the database with some fake stats (useful for testing or developing SimpleStats), you can use the panel interface to generate some in the "Information" tab.

#### Options
Like any Kirby plugin, options can be set in your `site/config/config.php`.
All available options are listed and explained in `src/config/options.php`.
Expand Down
50 changes: 50 additions & 0 deletions src/components/Sections/ListViewer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<template>
<div>
<ul class="" v-if="arrayData" v-bind:style="{ paddingLeft: (20*depth) + 'px' }">
<li
v-for="(value, key, index) in arrayData"
v-bind="arrayData.status"
:key="key"
class=""
v-bind:class="{ isChild: subpage, 'text-danger': hasError }"
>
<div v-if="(typeof value === 'Array' || typeof value === 'object')">
<span>
{{ key }} =
</span>
<list-viewer v-if="(typeof value === 'Array' || typeof value === 'object') && depth < 10" :array-data="value" :depth="depth+1" />
</div>
<div v-else>
<span>
{{key}} = {{value}}
</span>
</div>
</li>
</ul>
</div>
</template>

<script>
export default {
name: 'list-viewer', // Needed for self-recursiveness
props: {
'arrayData':{
type: Array,
default: []
},
'depth': {
type: Number,
default: 0
}
},
methods: {
}
};
</script>

<style>
</style>
67 changes: 59 additions & 8 deletions src/components/Sections/TrackingTester.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,60 @@
<k-text-field v-if="this.referrerResponse" :counter="false" :disabled="true" :label="$t('simplestats.info.tester.referrer.response.medium')" :value="formattedReferrerMedium" icon="globe"/>
<k-text-field v-if="this.referrerResponse" :counter="false" :disabled="true" :label="$t('simplestats.info.tester.referrer.response.url')" :value="formattedReferrerUrl" icon="globe"/>
</k-form>
<br />

<k-headline class="rightColumnAlign">{{ $t('simplestats.info.tester.generator') }}</k-headline>
<k-form @submit="generateStats">
<k-select-field v-model="generatorMode" :label="$t('simplestats.info.tester.generator.generatorMode')" :options="[
{ value: 'all', text: 'Static : all pages' },
{ value: 'randomsingle', text: 'Single random page' },
{ value: 'randommulti', text: 'Multiple random pages' }
]"/>
<k-date-field v-model="generatorFrom" :label="$t('simplestats.info.tester.generator.datefrom')" :time="false" />
<k-date-field v-model="generatorTo" :label="$t('simplestats.info.tester.generator.dateto')" :time="false" />
<k-field :translate="false" :label="$t('simplestats.info.tester.generator.unlockgenerator')">
<k-checkbox-input @input="acceptGenerate" :value="unlockGenerator" :style="{padding:'.5em'}" theme="field" :novalidate="true" />
<k-button @click="generateStats" class="" :style="{border:'1px solid black', padding: '0 1em', 'borderRadius': '3px'}">Go!</k-button>
</k-field>

<k-field :translate="false" label="Result" v-if="generatorResponse && generatorResponse.data">
<list-viewer :array-data="generatorResponse.data" data-theme="field" class="k-input" :style="{padding:'1em'}" />
</k-field>
<k-textarea-field v-model="generatorResponse.error" v-if="generatorResponse && generatorResponse.error" label="Error!" :buttons="false" :disabled="true" />

</k-form>
</div>
</template>

<script>
//import format from 'date-fns/format';
import ListViewer from "./ListViewer.vue";
export default {
extends: 'k-pages-section',
name: 'TrackingTester',
//extends: 'k-pages-section',
components: {
ListViewer
},
data() {
let now = new Date();
let before = new Date();
before.setDate(now.getDate()-30);
return {
isLoading: true,
error: "",
currentDevice: "",
isLoading: true,
error: "",
currentDevice: "",
currentUserAgent: this.currentUserAgentJS,
customDevice: "",
customUserAgent: "",
referrerField: "",
customDevice: "",
customUserAgent: "",
referrerField: "",
referrerResponse: null,
unlockGenerator: false,
generatorMode: 'randommulti',
generatorTo: now.toString(),
generatorFrom: before.toString(),
generatorResponse:null,
}
},
created() {
Expand Down Expand Up @@ -163,7 +200,21 @@ export default {
else this.customDevice = 'Loading Error !';
});
}
},
acceptGenerate(v){
this.unlockGenerator=v;
},
generateStats() {
this.$api
.get("simplestats/trackingtester/generatestats?proceed="+(this.unlockGenerator?'yes':'no')+"&from="+new Date(this.generatorFrom).getTime()*0.001+"&to="+new Date(this.generatorTo).getTime()*0.001+"&mode="+this.generatorMode )
.then(response => {
this.generatorResponse = response;
})
.catch(error => {
this.generatorResponse = {'status':'false', 'error': 'Loading Error = '+error.message};
});
},
}
};
</script>
Expand Down
53 changes: 53 additions & 0 deletions src/config/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,59 @@
}
}
],
[
'pattern' => 'simplestats/trackingtester/generatestats',
'method' => 'GET',
'action' => function () use ($kirby) {
if( $this->user()->hasSimpleStatsPanelAccess() ){

// Get time range info
$from = @$kirby->request()->query()->data()['from'];
$to = @$kirby->request()->query()->data()['to'];
if($from && $to){
// Parse range as date ? dd-mm-yyyy
if( strpos($from, '-')===2 && strpos($to, '-')===2 ){
$day=intval(substr($from, 0,2), 10);
$month=intval(substr($from, 3,2), 10);
$year=intval(substr($from, 6,4), 10);
$from = mktime(0,0,0,$month,$day,$year);
$day=intval(substr($to, 0,2), 10);
$month=intval(substr($to, 3,2), 10);
$year=intval(substr($to, 6,4), 10);
$to = mktime(0,0,0,$month,$day,$year);
}
// Parse as timestamp
else {
$from = intval($from, 10);
$to = intval($to, 10);
}

// Parse mode
$mode = @$kirby->request()->query()->data()['mode']??null;

// Confirm ?
$proceed = @$kirby->request()->query()->data()['proceed']??'';
if($proceed !== 'yes'){
return ['status'=>false, 'error'=>'Please confirm that the date ranges from '.date('d-M-Y', $from).' to '.date('d-M-Y', $to).'. (check that box!)'];//, adding &proceed=yes to the query param.'];
}

// go !
return StatsGenerator::GenerateVisits($from, $to, $mode);

//$uainfo = SimpleStats::detectSystemFromUA($str);
// if($uainfo && isset($uainfo['device'])) $uainfo['device'] = Stats::translateDeviceType($uainfo['device']);
// return $uainfo??'Invalid referrer url!';

//return ['status'=>false,'message'=>'ok ?'];
}

return ['status'=>false,'error'=>'No range !'];
}
else {
throw new PermissionException('You are not authorised to view statistics.');
}
}
],
[
'pattern' => 'simplestats/checkrequirements',
'method' => 'GET',
Expand Down
6 changes: 6 additions & 0 deletions src/config/i18n/en.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,10 @@
'simplestats.info.tester.referrer.response.source' => 'Identified Source',
'simplestats.info.tester.referrer.response.medium' => 'Type',
'simplestats.info.tester.referrer.response.url' => 'Detected url',
'simplestats.info.tester.generator' => 'Stats Generator',
'simplestats.info.tester.generator.unlockgenerator' => 'Allow generating fake stats in my database file',
'simplestats.info.tester.generator.mode' => 'Generator mode',
'simplestats.info.tester.generator.datefrom' => 'From date',
'simplestats.info.tester.generator.dateto' => 'To Date',

];
5 changes: 3 additions & 2 deletions src/config/usermethods.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace daandelange\SimpleStats;

return [
'hasSimpleStatsPanelAccess' => function () : bool {
'hasSimpleStatsPanelAccess' => function (bool $forSpecialAdminAccess = false) : bool {
$user = kirby()->user();
return
// Panel is active
Expand All @@ -14,6 +14,7 @@
&&
// user is authorized to view statistics
in_array( $user->role()->id(), option('daandelange.simplestats.panel.authorizedRoles', ['admin']) )
;
&&
( !$forSpecialAdminAccess || $user->isAdmin() );
}
];
28 changes: 17 additions & 11 deletions src/models/SimpleStats.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,28 @@
class SimpleStats extends SimpleStatsDb {

// Trigger track function
// Note : the uri should be an id, the Kirby uri is translateable.
public static function track( string $page_uri = '' ): bool {
// Note : the uri should be $page->id(), the Kirby uri is translateable.
// Additional params are not recommended to use; mainly for testing purposes.
public static function track( string $page_uri = '', int $time = null, \Kirby\Cms\User $user = null, string $forceLang = null ): bool {

// skip ignored paths
if( empty($page_uri) || in_array($page_uri, option('daandelange.simplestats.tracking.ignore.pages')) === true) {
return false;
}

// Format time
if(!$time) $time = time();

// tmp : Sync daystats
Stats::syncDayStats();
Stats::syncDayStats($time);

// Skip ignored roles
if( count(option('daandelange.simplestats.tracking.ignore.roles')) > 0){
$curUser = kirby()->user();
// Fetch user
if(!$user) $user = kirby()->user();
$ignores = option('daandelange.simplestats.tracking.ignore.roles');
if($curUser && $curUser->isLoggedIn()){
foreach($curUser->roles() as $role){
if($user && $user->isLoggedIn()){
foreach($user->roles() as $role){
if( in_array($role, $ignores)) return false;
}
}
Expand Down Expand Up @@ -74,14 +79,15 @@ public static function track( string $page_uri = '' ): bool {
}

$userEntry = $userResult->first();
//var_dump('$userEntry', $userEntry);

// Bot detection / ignore
$userIsBot = false;

// New user ?
if($userEntry===null){
// Default values
$timestamp = time();
$timestamp = $time;
$osfamily = $devicetype = $browserengine = '';
$visitedpages = '';

Expand All @@ -95,7 +101,7 @@ public static function track( string $page_uri = '' ): bool {

// Populate visited pages
if( option('daandelange.simplestats.tracking.enableVisits') === true ){
$visitedpages = self::getPageIDWithLang($page_uri);
$visitedpages = self::getPageIDWithLang($page_uri, $forceLang);
}

// Populate device info ?
Expand All @@ -121,7 +127,7 @@ public static function track( string $page_uri = '' ): bool {
// Append visited pages (except bots)
// Note: Bot visits are not tracked. Todo: Make this an option
if( !$userIsBot && option('daandelange.simplestats.tracking.enableVisits') === true ){
$page_uri = self::getPageIDWithLang($page_uri);
$page_uri = self::getPageIDWithLang($page_uri, $forceLang);

// Check if the page was already visited.
if( !in_array($page_uri, explode(',', $userEntry->visitedpages) )){
Expand Down Expand Up @@ -194,11 +200,11 @@ public static function track( string $page_uri = '' ): bool {
return true;
}

public static function getPageIDWithLang($page_uri): string {
public static function getPageIDWithLang($page_uri, string $forceLang = null): string {

// With language ?
if( kirby()->multilang() && option('daandelange.simplestats.tracking.enableVisitLanguages') === true ) {
$curLang = kirby()->language();
$curLang = kirby()->language($forceLang)??kirby()->language();
if(!$curLang) $curLang = 'none';
else $curLang = $curLang->code();
return $page_uri .'::'. $curLang;
Expand Down
8 changes: 5 additions & 3 deletions src/models/Stats.php
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ public static function pageStats(): ?array {
$globalLanguagesData =[];
$languagesOverTimeData=[];

// SYNC (todo: make this an option?)
// SYNC inline (todo: allow syncing in multiple ways [callback, crontab, inline..] )
self::syncDayStats(); // tmp

//$db = selfdatabase();
Expand Down Expand Up @@ -727,7 +727,7 @@ public static function pageStats(): ?array {
}

// Collect garbage, synthetize it and anonymously store it in permanent db
public static function syncDayStats(): bool {
public static function syncDayStats(int $time=null): bool {

// init db
//$db = self::database();
Expand All @@ -739,8 +739,10 @@ public static function syncDayStats(): bool {
$newEngines = [];
$newSystems = [];

if(!$time) $time = time();

// Get visitors older then 1 day
$yesterday = time() - option('daandelange.simplestats.tracking.uniqueSeconds', 24*60*60);
$yesterday = $time - option('daandelange.simplestats.tracking.uniqueSeconds', 24*60*60);
$visitors = self::database()->query("SELECT `userunique`, `visitedpages`, `osfamily`, `devicetype`, `browserengine`, `timeregistered` FROM `pagevisitors` WHERE `timeregistered` < ${yesterday} ORDER BY `timeregistered` ASC LIMIT 0,".SIMPLESTATS_DUMMY_DB_LIMIT);

if($visitors){
Expand Down

0 comments on commit 42135f1

Please sign in to comment.