From 54d90dbc803bc8a9a108481aa0f8b10ef7e71554 Mon Sep 17 00:00:00 2001 From: Benjamin Renard <benjamin.renard@akka.eu> Date: Tue, 29 Jun 2021 13:32:12 +0200 Subject: [PATCH] Add concurrent access to statistics files --- php/classes/AmdaStats.php | 285 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------ 1 file changed, 159 insertions(+), 126 deletions(-) diff --git a/php/classes/AmdaStats.php b/php/classes/AmdaStats.php index 08a0a79..2e753f0 100644 --- a/php/classes/AmdaStats.php +++ b/php/classes/AmdaStats.php @@ -6,8 +6,8 @@ class AmdaStats { - public $statXml; - public $tasks = array('plot', 'mining', 'print', 'statistic'); + public $statsFilePath; + public $tasks = array('plot', 'mining', 'print', 'statistics'); public $tasksWs = array('ws_print', 'ws_plot'); public $tasksAdd = array('ttoper', 'samp', 'upload', 'create', 'images'); public $usersToExclude = array('bouchemit'); @@ -17,92 +17,139 @@ class AmdaStats { 'getparameter'=>'ws_print', 'getdataset' => 'ws_print', 'getorbites' => 'ws_print', 'getplot' => 'ws_plot'); public function __construct($user) { - - $this->statXml = new DomDocument('1.0','UTF-8'); - $this->statXml->preserveWhiteSpace = false; - $this->statXml->formatOutput = true; - - if (!defined("StatsXml")){ - $thisYear = date("Y"); - if (!$user){ - // general - to read - // define('StatsXml',DATAPATH.'Statistics/Stats.xml'); - define("StatsXml",DATAPATH."Statistics/Stats$thisYear.xml"); - // if (file_exists(StatsXml)) unlink(StatsXml); - } - else { - // individual - to write - // define("StatsXml", USERPATH."/".$user."/Stats.xml"); - define("StatsXml", USERPATH.$user."/Stats$thisYear.xml"); - $this->user = $user; - } + if (!is_dir(DATAPATH.'Statistics')) { + if (!mkdir(DATAPATH.'Statistics', 0775)) $this->success = false; //return -1; + if (!chgrp(DATAPATH.'Statistics', APACHE_USER)) $this->success = false; // return -1; } - if (!file_exists(StatsXml)) { - if (!is_dir(DATAPATH.'Statistics')) { - if (!mkdir(DATAPATH.'Statistics', 0775)) $this->success = false; //return -1; - if (!chgrp(DATAPATH.'Statistics', APACHE_USER)) $this->success = false; // return -1; - } + $this->statsFilePath = $this->getStatsFilePath($user); + } - $status = $this->generateXml(); - if (!$status) { - error_log('Cannot create Stats.xml: Fatal Error '.$user,1,email); - $this->success = false; - } + public function addTask($user, $task, $vars) { + $this->concurrentAccessGuestFile(array($this,'_addTask'),array('user' => $user, 'task' => $task, 'vars' => $vars)); + } + + public function getModulesStat($start, $stop, $update) { + return $this->concurrentAccessGuestFile(array($this,'_getModulesStat'), array('start' => $start, 'stop' => $stop, 'update' => $update)); + } + + public function mergeXml($year) { + return $this->concurrentAccessGuestFile(array($this,'_mergeXml'), array('year' => $year)); + } + + public function getDataStat($index, $start, $stop, $update) { + return $this->concurrentAccessGuestFile(array($this,'_getDataStat'), array('index' => $index, 'start', $start, 'stop' => $stop, 'update' => $update)); + } + + public function mergeStats($inXml) { + return $this->concurrentAccessGuestFile(array($this,'_mergeStats'), array('inXml' => $inXml)); + } + + private function getStatsFilePath($user) { + $thisYear = date("Y"); + if (empty($user)){ + return DATAPATH."Statistics/Stats$thisYear.xml"; } - else { - $status = $this->statXml->load(StatsXml); - if (!$status) { - $status = $this->generateXml(); - $msg = $status ? 'Cannot load Stats.xml. New Stats.xml was created' : - 'Cannot load Stats.xml. Cannot create Stats.xml: Fatal Error '; - - error_log($msg.$user,1,email); - - if (!$status) - $this->success = false; + return USERPATH.$user."/Stats$thisYear.xml"; + } + + private function concurrentAccessGuestFile($callback, $options) { + $lockFile = $this->statsFilePath.".lockfile"; + + $fp = fopen($lockFile, "w+"); + + if ($fp === false) { + return false; + } + + $res = true; + + if (flock($fp, LOCK_EX)) + { + $newFile = FALSE; + if (!file_exists($this->statsFilePath)) { + $res = $this->_generateXml(); } - else { - $allTasks = array_merge($this->tasks, $this->tasksAdd, $this->tasksWs); - $newTask = FALSE; - foreach ($allTasks as $task) { - $items = $this->statXml->getElementsByTagName($task); - if ($items->length == 0) { - //add missing task - $element = $this->statXml->createElement("$task"); - $this->statXml->documentElement->appendChild($element); - $newTask = TRUE; + + if ($res) { + $dom = new DomDocument("1.0","UTF-8"); + $dom->preserveWhiteSpace = false; + $dom->formatOutput = true; + $res = $dom->load($this->statsFilePath); + if (!$newFile) { + // add missing tasks if needed + $allTasks = array_merge($this->tasks, $this->tasksAdd, $this->tasksWs); + $newTask = FALSE; + foreach ($allTasks as $task) { + $items = $dom->getElementsByTagName($task); + if ($items->length == 0) { + //add missing task + $element = $dom->createElement("$task"); + $dom->documentElement->appendChild($element); + $newTask = TRUE; + } } + if ($newTask) { + $dom->save($this->statsFilePath); + } } - if ($newTask) { - $this->statXml->save(StatsXml); + if ($res) { + $func_res = call_user_func($callback,$dom, $options); } } - } + } + else + $res = false; + + fclose($fp); + + if ($res) + return $func_res; + + return false; + } + + private function _generateXml() { + $dom = new DomDocument("1.0","UTF-8"); + $dom->preserveWhiteSpace = false; + $dom->formatOutput = true; + + $rootElement = $dom->createElement('stats'); + + $allTasks = array_merge($this->tasks, $this->tasksAdd, $this->tasksWs); + + foreach ($allTasks as $task) { + $element = $dom->createElement("$task"); + $rootElement->appendChild($element); + } + + $dom->appendChild($rootElement); + + return $dom->save($this->statsFilePath); } /* * Merge individual User Stats.xml into one generique Stats.xml */ - public function mergeXml($year) { + private function _mergeXml($dom, $options) { // long procedure ini_set('max_execution_time', 600); $allTasks = array_merge($this->tasks, $this->tasksAdd, $this->tasksWs); $userDoc = new DomDocument("1.0"); - if ($year == null) $year = date("Y"); + if ($options['year'] == null) $options['year'] = date("Y"); $users=glob(USERPATH."*"); foreach ($users as $user) { - $userXmlPath = $user."/Stats$year.xml"; + $userXmlPath = $user."/Stats".$options['year'].".xml"; if (!file_exists($userXmlPath)) continue; $userDoc->load($userXmlPath); foreach ($allTasks as $task) { - $globalTaskItems = $this->statXml->getElementsByTagName($task); + $globalTaskItems = $dom->getElementsByTagName($task); if ($globalTaskItems->length == 0) continue; $globalTaskItem = $globalTaskItems->item(0); @@ -113,7 +160,7 @@ class AmdaStats { $userItems = $userTaskItem->getElementsByTagName("item"); if ($userItems->length > 0) { foreach ($userItems as $userItem) { - $globalItem = $this->statXml->importNode($userItem, true); + $globalItem = $dom->importNode($userItem, true); $globalTaskItem->appendChild($globalItem); } } @@ -121,98 +168,79 @@ class AmdaStats { } // write task statistics as json - $this->getModulesStat(null,null,true); + $this->_getModulesStat($dom, array('start' => null, 'stop' => null, 'update' => TRUE)); // write data statistics as json - $this->getDataStat(0,null,null,true); + $this->_getDataStat($dom, array('index' => 0, 'start', null, 'stop' => null, 'update' => true)); - return $this->statXml->save(StatsXml); + return $dom->save($this->statsFilePath); } - private function generateXml() { - - $rootElement = $this->statXml->createElement('stats'); - - $allTasks = array_merge($this->tasks, $this->tasksAdd, $this->tasksWs); - - foreach ($allTasks as $task) { - $element = $this->statXml->createElement("$task"); - $rootElement->appendChild($element); - } - - $this->statXml->appendChild($rootElement); - - return $this->statXml->save(StatsXml); - } - - public function addTask($user, $task, $vars) { - -// if (!$this->user) { -// error_log('User is null', 1, email); -// return; -// } - if ($task == 'killplot') - return; + private function _addTask($dom, $options) { + if ($options['task'] == 'killplot') + return FALSE; - if ($vars) - $realTask = $this->task[$task]; + if ($options['vars']) + $realTask = $this->task[$options['task']]; else - $realTask = $task; + $realTask = $options['task']; - if (!in_array($user, $this->usersToExclude)) { - $taskElementNode = $this->statXml->getElementsByTagName("$realTask"); + if (!in_array($options['user'], $this->usersToExclude)) { + $taskElementNode = $dom->getElementsByTagName("$realTask"); if ($taskElementNode->length < 1) return; $taskElement = $taskElementNode->item(0); if (is_object($taskElement)) { - $newTask = $this->statXml->createElement('item'); + $newTask = $dom->createElement('item'); $newTask->setAttribute('date', date('Y-m-d')); - $newTask->setAttribute('user', $user); + $newTask->setAttribute('user', $options['user']); - if ($vars) { + if ($options['vars']) { $ID = array(); - foreach ($vars as $var) { + foreach ($options['vars'] as $var) { $ID[] = $var; } $ID = array_unique($ID); foreach ($ID as $id) { - $datasetElement = $this->statXml->createElement('dataset', $id); + $datasetElement = $dom->createElement('dataset', $id); $newTask->appendChild($datasetElement); } } $taskElement->appendChild($newTask); - $this->statXml->save(StatsXml); + return $dom->save($this->statsFilePath); } else - error_log('Check Stats.xml - no task element '.$task, 1, email); + error_log('Check Stats.xml - no task element '.$options['task'], 1, email); } + + return FALSE; } /* * Show Statistics */ - public function getModulesStat($start, $stop, $update){ + private function _getModulesStat($dom, $options){ - if (!$update && file_exists(DATAPATH.'Statistics/tasks.json')) { - return file_get_contents(DATAPATH.'Statistics/tasks.json'); + if (!$options['update'] && file_exists(DATAPATH.'Statistics/tasks.json')) { + return file_get_contents(DATAPATH.'Statistics/tasks.json'); } $taskArray = array(); foreach (array_merge($this->tasks,$this->tasksAdd, $this->tasksWs) as $task) { - $taskItems = $this->statXml->getElementsByTagName($task); + $taskItems = $dom->getElementsByTagName($task); if ($taskItems->length < 1) return; $theTask = $taskItems->item(0); $items = $theTask->getElementsByTagName('item'); $hints = $items->length; - $startStop = $this->getStartStop($items, $start, $stop); + $startStop = $this->getStartStop($items, $options['start'], $options['stop']); $taskArray[] = array('task' => $task, 'number' => $hints, 'start' => $startStop[0], 'stop' => $startStop[1]); @@ -228,9 +256,9 @@ class AmdaStats { /* * Show Statistics */ - public function getDataStat($index, $start, $stop, $update){ + private function _getDataStat($dom, $options){ - if (!$update && file_exists(DATAPATH.'Statistics/data.json')) { + if (!$options['update'] && file_exists(DATAPATH.'Statistics/data.json')) { $GENERALarray = json_decode(file_get_contents(DATAPATH.'Statistics/data.json')); } else { @@ -243,7 +271,7 @@ class AmdaStats { $dataTasks = array_merge($this->tasks, $this->tasksWs); foreach ($dataTasks as $task) { - $taskItems = $this->statXml->getElementsByTagName($task); + $taskItems = $dom->getElementsByTagName($task); if ($taskItems->length < 1) continue; $theTask = $taskItems->item(0); @@ -258,10 +286,15 @@ class AmdaStats { foreach ($VIs as $VI) { $id = $VI->nodeValue; if ($id) { - + if (!in_array($id, $usersArray)) { + $usersArray[$id] = array(); + } + if (!in_array($user, $usersArray[$id])) { + $usersArray[$id][$user] = 0; + } $usersArray[$id][$user]++; - if ($TASKarray[$id]) { + if (array_key_exists($id, $TASKarray)) { $TASKarray[$id]++; $TOTALarray[$id]++; if ($STARTarray[$id] > $time) @@ -270,7 +303,7 @@ class AmdaStats { $STOParray[$id] = $time; } else { - if (!$TOTALarray[$id]) { + if (!array_key_exists($id,$TOTALarray)) { $STARTarray[$id] = $time; $STOParray[$id] = $time; $TOTALarray[$id] = 1; @@ -298,17 +331,20 @@ class AmdaStats { foreach ($TOTALarray as $key => $value) { $viStart = $STARTarray[$key]; $viStop = $STOParray[$key]; - $plot = $VIarray['plot'][$key]; - $mining = $VIarray['mining'][$key]; - $print = $VIarray['print'][$key]; - $stat = $VIarray['statistics'][$key]; + $plot = (empty($VIarray['plot']) || empty($VIarray['plot'][$key])) ? 0 : $VIarray['plot'][$key]; + $mining = (empty($VIarray['mining']) || empty($VIarray['mining'][$key])) ? 0 : $VIarray['mining'][$key]; + $print = (empty($VIarray['print']) || empty($VIarray['print'][$key])) ? 0 : $VIarray['print'][$key]; + $stat = (empty($VIarray['statistics']) || empty($VIarray['statistics'][$key])) ? 0 : $VIarray['statistics'][$key]; + $ws_print = (empty($VIarray['ws_print']) || empty($VIarray['ws_print'][$key])) ? 0 : $VIarray['ws_print'][$key]; + $ws_plot = (empty($VIarray['ws_plot']) || empty($VIarray['ws_plot'][$key])) ? 0 : $VIarray['ws_plot'][$key]; $uniqueUsers = count($usersArray[$key]); if ($key != 'undefined') { $GENERALarray[] = array('id' => $key, 'number' => $value, 'percent' => $value, 'plot' => $plot, 'mining' => $mining, - 'print' => $print,'statistics' => $stat, + 'print' => $print,'statistics' => $stat, + 'ws_print' => $ws_print, 'ws_plot' => $ws_plot, 'start' => $viStart, 'stop' => $viStop, 'unique' => $uniqueUsers); $Ntotal += $value; @@ -325,8 +361,8 @@ class AmdaStats { $Nmax = count($GENERALarray); - $length = $index + 20 > $Nmax ? $Nmax - $index + 1 : 20; - $objToReturn = array('stats' => array_reverse(array_slice($GENERALarray, $index, $length))); + $length = $options['index'] + 20 > $Nmax ? $Nmax - $options['index'] + 1 : 20; + $objToReturn = array('stats' => array_reverse(array_slice($GENERALarray, $options['index'], $length))); file_put_contents(DATAPATH.'Statistics/data.json',json_encode($GENERALarray)); @@ -355,32 +391,29 @@ class AmdaStats { return array(min($date), max($date)); } - public function mergeStats($inXml) { + private function _mergeStats($dom, $options) { - if (!file_exists(StatsXml)) return 0; - if (!file_exists($inXml)) return 0; + if (!file_exists($options['inXml'])) return 0; $tags = array_merge($this->tasks,$this->tasksAdd, $this->tasksWs); - $doc1 = new DomDocument("1.0"); $doc2 = new DomDocument("1.0"); - if (!$doc1->load(StatsXml)) return 0; - if (!$doc2->load($inXml)) return 0; + if (!$doc2->load($options['inXml'])) return 0; foreach ($tags as $tag){ - $tag1 = $doc1->getElementsByTagName($tag)->item(0); + $tag1 = $dom->getElementsByTagName($tag)->item(0); $tag2 = $doc2->getElementsByTagName($tag)->item(0); $items2 = $tag2->getElementsByTagName("item"); if ($items2->length > 0) { foreach ($items2 as $item2) { - $item1 = $doc1->importNode($item2, true); + $item1 = $dom->importNode($item2, true); $tag1->appendChild($item1); } } } - return $doc1->save(StatsXml); + return $dom->save($this->statsFilePath); } } ?> -- libgit2 0.21.2