StatsTable.php 8.17 KB
<?php
namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\I18n\FrozenTime;

/**
 * Stats Model
 *
 * @property \App\Model\Table\UsersTable|\Cake\ORM\Association\BelongsTo $Users
 *
 * @method \App\Model\Entity\Stat get($primaryKey, $options = [])
 * @method \App\Model\Entity\Stat newEntity($data = null, array $options = [])
 * @method \App\Model\Entity\Stat[] newEntities(array $data, array $options = [])
 * @method \App\Model\Entity\Stat|bool save(\Cake\Datasource\EntityInterface $entity, $options = [])
 * @method \App\Model\Entity\Stat saveOrFail(\Cake\Datasource\EntityInterface $entity, $options = [])
 * @method \App\Model\Entity\Stat patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
 * @method \App\Model\Entity\Stat[] patchEntities($entities, array $data, array $options = [])
 * @method \App\Model\Entity\Stat findOrCreate($search, callable $callback = null, $options = [])
 */
class StatsTable extends Table
{
    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->setTable('stats');
        $this->setDisplayField('year');
        $this->setPrimaryKey(['year', 'user_id']);

        $this->belongsTo('Users', [
            'foreignKey' => 'user_id',
            'joinType' => 'INNER'
        ]);
        
        /* FK composite possible aussi, ex :
        $this->belongsTo('Students', [
            'foreignKey' => ['program_id', 'location_id', 'course_id'],
        ]);
        */
    }

    /**
     * Default validation rules.
     *
     * @param \Cake\Validation\Validator $validator Validator instance.
     * @return \Cake\Validation\Validator
     */
    public function validationDefault(Validator $validator)
    {
        $validator
            ->scalar('year')
            ->allowEmptyString('year', 'create');

        $validator
            ->dateTime('last_login_time')
            ->allowEmptyDateTime('last_login_time');

        $validator
            ->dateTime('last_logout_time')
            ->allowEmptyDateTime('last_logout_time');

        $validator
            ->integer('connex_nb')
            ->allowEmptyString('connex_nb', false);

        $validator
            ->integer('connex_dur')
            ->allowEmptyString('connex_dur', false);

        return $validator;
    }

    /**
     * Returns a rules checker object that will be used for validating
     * application integrity.
     *
     * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
     * @return \Cake\ORM\RulesChecker
     */
    public function buildRules(RulesChecker $rules)
    {
        $rules->add($rules->existsIn(['user_id'], 'Users'));

        /* Rule avec composite key :
        $rules->add($rules->existsIn([’site_id’, ’article_id’], ’articles’))
        */
        
        return $rules;
    }
    
    
    private function getEntityForUser($user_id, $now) {
        //$now = new FrozenTime();
        //debug($last_login_time);
        $year = $now->format('Y'); // 2021
        //debug("year");debug($year);
        
        // On recup la Stat du user courant
        //$table_stats = $this->Users->Stats;
        $table_stats = $this;
        $stat_id = [$year,$user_id];
        $current_user_stat = null;
        try {
            // Si un enregistrement existe pour ce user (et cette année) => on le récupère
            $current_user_stat = $table_stats->get($stat_id);
            //debug("exists");
            //$current_user_stat->last_logout_time = null;
        } catch (\Cake\Datasource\Exception\RecordNotFoundException $e) {
            //echo 'Exception reçue : ',  $e->getMessage(), "\n";
            // Pas d'enregistrement pour ce user (et cette année) => on le crée
            //debug("new");
            /*
             $stat = [
             //['year','user_id'] => [$year,$user->id],
             //'(year,user_id)' => [$year,$user->id],
             'year' => $year,
             'user_id' => $user->id,
             'last_login_time' => $last_login_time,
             ];
             debug($stat);
             */
            //$stat_entity = $table_stats->newEntity($stat);
            $current_user_stat = $table_stats->newEntity();
            $current_user_stat->year = $year;
            $current_user_stat->user_id = $user_id;
        }
        return  $current_user_stat;
    }
    
    private function saveStat($stat, $event_name) {
        if (! $this->save($stat)) {
            echo "Impossible de sauvegarder une stat $event_name !";
            debug($stat->getErrors());
            debug($stat);
            exit;
        }
    }
    
    /*
     * Mise à jour des stats pour le user courant (et pour l'année courante) 
     * au moment de l'événement $event_name qui peut être de 3 types :
     * - 'sur login'
     * - 'durant la session'
     * - 'sur logout'
     */
    public function updateForUserWhen($user_id, $event_name) {
        
        // 1) Get (or create) stat for user $user_id
        
        $now = new FrozenTime();
        //debug("now");debug($now);
        $current_user_stat = $this->getEntityForUser($user_id, $now);
        
        
        // 2) TT selon event $event_name
        
        /*
         * - au LOGIN ( == 'sur login')
         * 
         * => on enregistre son heure de connexion
         * => on incrémente le nombre de connexions
         *
         */
        if ($event_name == 'sur login') {
            $last_login_time = $current_user_stat->last_login_time;
            $last_logout_time = $current_user_stat->last_logout_time;
            /*
             * Check le cas où le logout n'a pas été enregistré
             * (le user n'a pas fait logout, ça s'est fait tout seul par timeout)
             * => Dans ce cas, on ajoute à la durée totale le dernier temps de connexion last_connex_dur
             */
            if ($last_logout_time < $last_login_time) {
                $last_connex_dur = $current_user_stat->last_connex_dur;
                /*
                 * (Optimisation): Si la dernière session a été très courte (<20s), on ne le comptabilise pas
                 * => on décrémente le nb de connexions (passées)
                 */
                if ($current_user_stat->connex_nb>0 && $last_connex_dur<20)
                    $current_user_stat->connex_nb--;
                else
                    $current_user_stat->connex_dur_tot += $last_connex_dur;
            }
            // On met à jour le champs last_login_time, RAZ last_connex_dur, et incrémente le nb de connexions
            $current_user_stat->last_login_time = $now;
            $current_user_stat->last_connex_dur = 0;
            // et 1 NOUVELLE connexion de plus, 1 !
            $current_user_stat->connex_nb++;
        }
        
        /*
         * - Pendant la session OU BIEN au logout
         * 
         * => on met à jour son temps de connexion last_connex_dur
         * 
         * Si logout :
         * => on ajoute son temps de connexion (last_connex_dur) au champ qui totalise ce temps (connex_dur_tot)
         * => on enregistre son heure de dé-connexion (last_logout_time)
         *
         */
        else { 
            
            // - On met à jour le temps de connexion
            $last_login_time = $current_user_stat->last_login_time;
            $connex_duration = $now->diff($last_login_time);
            $connex_duration = $connex_duration->h*3600 + $connex_duration->i*60 + $connex_duration->s;
            $current_user_stat->last_connex_dur = $connex_duration;
            
            if ($event_name == 'sur logout') {
                // 1) On met à jour le champ last_logout_time
                $current_user_stat->last_logout_time = $now;
                // 2) On met à jour le temps de connexion total (cumulé)
                $current_user_stat->connex_dur_tot += $connex_duration;
            }
        
        } // Pendant la session OU BIEN au logout
            
        
        // 3) Sauvegarde en table
        
        $this->saveStat($current_user_stat, $event_name);
        
    } // updateForUserWhen()
        
    
}