123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- <?php
- /* Copyright (C) 2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
- * Copyright (c) 2008-2013 Laurent Destailleur <eldy@users.sourceforge.net>
- * Copyright (C) 2012 Regis Houssin <regis.houssin@capnetworks.com>
- * Copyright (C) 2012 Marcos García <marcosgdf@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /**
- * \file htdocs/core/class/stats.class.php
- * \ingroup core
- * \brief Common class to manage statistics reports
- */
- /**
- * Parent class of statistics class
- */
- abstract class Stats
- {
- protected $db;
- var $_lastfetchdate=array(); // Dates of cache file read by methods
- var $cachefilesuffix=''; // Suffix to add to name of cache file (to avoid file name conflicts)
- /**
- * Return nb of elements by month for several years
- *
- * @param int $endyear Start year
- * @param int $startyear End year
- * @param int $cachedelay Delay we accept for cache file (0=No read, no save of cache, -1=No read but save)
- * @return array Array of values
- */
- function getNbByMonthWithPrevYear($endyear,$startyear,$cachedelay=0)
- {
- global $conf,$user,$langs;
- if ($startyear > $endyear) return -1;
- $datay=array();
- // Search into cache
- if (! empty($cachedelay))
- {
- include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
- include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
- }
- $newpathofdestfile=$conf->user->dir_temp.'/'.get_class($this).'_'.__FUNCTION__.'_'.(empty($this->cachefilesuffix)?'':$this->cachefilesuffix.'_').$langs->defaultlang.'_entity.'.$conf->entity.'_user'.$user->id.'.cache';
- $newmask='0644';
- $nowgmt = dol_now();
- $foundintocache=0;
- if ($cachedelay > 0)
- {
- $filedate=dol_filemtime($newpathofdestfile);
- if ($filedate >= ($nowgmt - $cachedelay))
- {
- $foundintocache=1;
- $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$filedate;
- }
- else
- {
- dol_syslog(get_class($this).'::'.__FUNCTION__." cache file ".$newpathofdestfile." is not found or older than now - cachedelay (".$nowgmt." - ".$cachedelay.") so we can't use it.");
- }
- }
- // Load file into $data
- if ($foundintocache) // Cache file found and is not too old
- {
- dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate.".");
- $data = json_decode(file_get_contents($newpathofdestfile), true);
- }
- else
- {
- $year=$startyear;
- while ($year <= $endyear)
- {
- $datay[$year] = $this->getNbByMonth($year);
- $year++;
- }
- $data = array();
- for ($i = 0 ; $i < 12 ; $i++)
- {
- $data[$i][]=$datay[$endyear][$i][0];
- $year=$startyear;
- while($year <= $endyear)
- {
- $data[$i][]=$datay[$year][$i][1];
- $year++;
- }
- }
- }
- // Save cache file
- if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1))
- {
- dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk.");
- if (! dol_is_dir($conf->user->dir_temp)) dol_mkdir($conf->user->dir_temp);
- $fp = fopen($newpathofdestfile, 'w');
- fwrite($fp, json_encode($data));
- fclose($fp);
- if (! empty($conf->global->MAIN_UMASK)) $newmask=$conf->global->MAIN_UMASK;
- @chmod($newpathofdestfile, octdec($newmask));
- $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$nowgmt;
- }
- // return array(array('Month',val1,val2,val3),...)
- return $data;
- }
- /**
- * Return amount of elements by month for several years.
- * Criterias used to build request are defined into the constructor of parent class into xxx/class/xxxstats.class.php
- * The caller of class can add more filters into sql request by adding criteris into the $stats->where property just after
- * calling constructor.
- *
- * @param int $endyear Start year
- * @param int $startyear End year
- * @param int $cachedelay Delay we accept for cache file (0=No read, no save of cache, -1=No read but save)
- * @return array Array of values
- */
- function getAmountByMonthWithPrevYear($endyear,$startyear,$cachedelay=0)
- {
- global $conf,$user,$langs;
- if ($startyear > $endyear) return -1;
- $datay=array();
- // Search into cache
- if (! empty($cachedelay))
- {
- include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
- include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
- }
- $newpathofdestfile=$conf->user->dir_temp.'/'.get_class($this).'_'.__FUNCTION__.'_'.(empty($this->cachefilesuffix)?'':$this->cachefilesuffix.'_').$langs->defaultlang.'_entity.'.$conf->entity.'_user'.$user->id.'.cache';
- $newmask='0644';
- $nowgmt = dol_now();
- $foundintocache=0;
- if ($cachedelay > 0)
- {
- $filedate=dol_filemtime($newpathofdestfile);
- if ($filedate >= ($nowgmt - $cachedelay))
- {
- $foundintocache=1;
- $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$filedate;
- }
- else
- {
- dol_syslog(get_class($this).'::'.__FUNCTION__." cache file ".$newpathofdestfile." is not found or older than now - cachedelay (".$nowgmt." - ".$cachedelay.") so we can't use it.");
- }
- }
- // Load file into $data
- if ($foundintocache) // Cache file found and is not too old
- {
- dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate.".");
- $data = json_decode(file_get_contents($newpathofdestfile), true);
- }
- else
- {
- $year=$startyear;
- while($year <= $endyear)
- {
- $datay[$year] = $this->getAmountByMonth($year);
- $year++;
- }
- $data = array();
- // $data = array('xval'=>array(0=>xlabel,1=>yval1,2=>yval2...),...)
- for ($i = 0 ; $i < 12 ; $i++)
- {
- $data[$i][]=$datay[$endyear][$i][0]; // set label
- $year=$startyear;
- while($year <= $endyear)
- {
- $data[$i][]=$datay[$year][$i][1]; // set yval for x=i
- $year++;
- }
- }
- }
- // Save cache file
- if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1))
- {
- dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk.");
- if (! dol_is_dir($conf->user->dir_temp)) dol_mkdir($conf->user->dir_temp);
- $fp = fopen($newpathofdestfile, 'w');
- if ($fp)
- {
- fwrite($fp, json_encode($data));
- fclose($fp);
- if (! empty($conf->global->MAIN_UMASK)) $newmask=$conf->global->MAIN_UMASK;
- @chmod($newpathofdestfile, octdec($newmask));
- }
- else dol_syslog("Failed to write cache file", LOG_ERR);
- $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$nowgmt;
- }
- return $data;
- }
- /**
- * Return average of entity by month for several years
- *
- * @param int $endyear Start year
- * @param int $startyear End year
- * @return array Array of values
- */
- function getAverageByMonthWithPrevYear($endyear,$startyear)
- {
- if ($startyear > $endyear) return -1;
- $datay=array();
- $year=$startyear;
- while($year <= $endyear)
- {
- $datay[$year] = $this->getAverageByMonth($year);
- $year++;
- }
- $data = array();
- for ($i = 0 ; $i < 12 ; $i++)
- {
- $data[$i][]=$datay[$endyear][$i][0];
- $year=$startyear;
- while($year <= $endyear)
- {
- $data[$i][]=$datay[$year][$i][1];
- $year++;
- }
- }
- return $data;
- }
- /**
- * Return count, and sum of products
- *
- * @param int $year Year
- * @param int $cachedelay Delay we accept for cache file (0=No read, no save of cache, -1=No read but save)
- * @return array Array of values
- */
- function getAllByProductEntry($year,$cachedelay=0)
- {
- global $conf,$user,$langs;
- $datay=array();
- // Search into cache
- if (! empty($cachedelay))
- {
- include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
- include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
- }
- $newpathofdestfile=$conf->user->dir_temp.'/'.get_class($this).'_'.__FUNCTION__.'_'.(empty($this->cachefilesuffix)?'':$this->cachefilesuffix.'_').$langs->defaultlang.'_entity.'.$conf->entity.'_user'.$user->id.'.cache';
- $newmask='0644';
- $nowgmt = dol_now();
- $foundintocache=0;
- if ($cachedelay > 0)
- {
- $filedate=dol_filemtime($newpathofdestfile);
- if ($filedate >= ($nowgmt - $cachedelay))
- {
- $foundintocache=1;
- $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$filedate;
- }
- else
- {
- dol_syslog(get_class($this).'::'.__FUNCTION__." cache file ".$newpathofdestfile." is not found or older than now - cachedelay (".$nowgmt." - ".$cachedelay.") so we can't use it.");
- }
- }
- // Load file into $data
- if ($foundintocache) // Cache file found and is not too old
- {
- dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate.".");
- $data = json_decode(file_get_contents($newpathofdestfile), true);
- }
- else
- {
- $data=$this->getAllByProduct($year);
- // $data[$i][]=$datay[$year][$i][1]; // set yval for x=i
- }
- // Save cache file
- if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1))
- {
- dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk.");
- if (! dol_is_dir($conf->user->dir_temp)) dol_mkdir($conf->user->dir_temp);
- $fp = fopen($newpathofdestfile, 'w');
- if ($fp)
- {
- fwrite($fp, json_encode($data));
- fclose($fp);
- if (! empty($conf->global->MAIN_UMASK)) $newmask=$conf->global->MAIN_UMASK;
- @chmod($newpathofdestfile, octdec($newmask));
- }
- $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$nowgmt;
- }
- return $data;
- }
- // Here we have low level of shared code called by XxxStats.class.php
- /**
- * Return nb of elements by year
- *
- * @param string $sql SQL request
- * @return array
- */
- function _getNbByYear($sql)
- {
- $result = array();
- dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
- $resql=$this->db->query($sql);
- if ($resql)
- {
- $num = $this->db->num_rows($resql);
- $i = 0;
- while ($i < $num)
- {
- $row = $this->db->fetch_row($resql);
- $result[$i] = $row;
- $i++;
- }
- $this->db->free($resql);
- }
- else {
- dol_print_error($this->db);
- }
- return $result;
- }
- /**
- * Return nb of elements, total amount and avg amount each year
- *
- * @param string $sql SQL request
- * @return array Array with nb, total amount, average for each year
- */
- function _getAllByYear($sql)
- {
- $result = array();
- dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
- $resql=$this->db->query($sql);
- if ($resql)
- {
- $num = $this->db->num_rows($resql);
- $i = 0;
- while ($i < $num)
- {
- $row = $this->db->fetch_object($resql);
- $result[$i]['year'] = $row->year;
- $result[$i]['nb'] = $row->nb;
- if($i>0 && $row->nb) $result[$i-1]['nb_diff'] = ($result[$i-1]['nb'] - $row->nb) / $row->nb * 100;
- $result[$i]['total'] = $row->total;
- if($i>0 && $row->total) $result[$i-1]['total_diff'] = ($result[$i-1]['total'] - $row->total) / $row->total * 100;
- $result[$i]['avg'] = $row->avg;
- if($i>0 && $row->avg) $result[$i-1]['avg_diff'] = ($result[$i-1]['avg'] - $row->avg) / $row->avg * 100;
- // For some $sql only
- if (isset($row->weighted))
- {
- $result[$i]['weighted'] = $row->weighted;
- if($i>0 && $row->weighted) $result[$i-1]['avg_weighted'] = ($result[$i-1]['weighted'] - $row->weighted) / $row->weighted * 100;
- }
- $i++;
- }
- $this->db->free($resql);
- }
- else {
- dol_print_error($this->db);
- }
- return $result;
- }
- /**
- * Renvoie le nombre de proposition par mois pour une annee donnee
- *
- * @param int $year Year
- * @param string $sql SQL
- * @param int $format 0=Label of absiss is a translated text, 1=Label of absiss is a number
- * @return array Array of nb each month
- */
- function _getNbByMonth($year, $sql, $format=0)
- {
- $result=array();
- $res=array();
- dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
- $resql=$this->db->query($sql);
- if ($resql)
- {
- $num = $this->db->num_rows($resql);
- $i = 0; $j = 0;
- while ($i < $num)
- {
- $row = $this->db->fetch_row($resql);
- $j = $row[0] * 1;
- $result[$j] = $row[1];
- $i++;
- }
- $this->db->free($resql);
- }
- else
- {
- dol_print_error($this->db);
- }
- for ($i = 1 ; $i < 13 ; $i++)
- {
- $res[$i] = (isset($result[$i])?$result[$i]:0);
- }
- $data = array();
- for ($i = 1 ; $i < 13 ; $i++)
- {
- $month=dol_print_date(dol_mktime(12,0,0,$i,1,$year),($format?"%m":"%b"));
- $month=dol_substr($month,0,3);
- $data[$i-1] = array($month, $res[$i]);
- }
- return $data;
- }
- /**
- * Renvoie le nombre d'element par mois pour une annee donnee
- *
- * @param int $year Year
- * @param string $sql SQL
- * @param int $format 0=Label of absiss is a translated text, 1=Label of absiss is a number
- * @return array
- */
- function _getAmountByMonth($year, $sql, $format=0)
- {
- $result=array();
- $res=array();
- dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
- $resql=$this->db->query($sql);
- if ($resql)
- {
- $num = $this->db->num_rows($resql);
- $i = 0;
- while ($i < $num)
- {
- $row = $this->db->fetch_row($resql);
- $j = $row[0] * 1;
- $result[$j] = $row[1];
- $i++;
- }
- $this->db->free($resql);
- }
- else dol_print_error($this->db);
- for ($i = 1 ; $i < 13 ; $i++)
- {
- $res[$i] = (int) round((isset($result[$i])?$result[$i]:0));
- }
- $data = array();
- for ($i = 1 ; $i < 13 ; $i++)
- {
- $month=dol_print_date(dol_mktime(12,0,0,$i,1,$year),($format?"%m":"%b"));
- $month=dol_substr($month,0,3);
- $data[$i-1] = array($month, $res[$i]);
- }
- return $data;
- }
- /**
- * Renvoie le montant moyen par mois pour une annee donnee
- *
- * @param int $year Year
- * @param string $sql SQL
- * @param int $format 0=Label of absiss is a translated text, 1=Label of absiss is a number
- * @return array
- */
- function _getAverageByMonth($year, $sql, $format=0)
- {
- $result=array();
- $res=array();
- dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
- $resql=$this->db->query($sql);
- if ($resql)
- {
- $num = $this->db->num_rows($resql);
- $i = 0; $j = 0;
- while ($i < $num)
- {
- $row = $this->db->fetch_row($resql);
- $j = $row[0] * 1;
- $result[$j] = $row[1];
- $i++;
- }
- $this->db->free($resql);
- }
- else dol_print_error($this->db);
- for ($i = 1 ; $i < 13 ; $i++)
- {
- $res[$i] = (isset($result[$i])?$result[$i]:0);
- }
- $data = array();
- for ($i = 1 ; $i < 13 ; $i++)
- {
- $month=dol_print_date(dol_mktime(12,0,0,$i,1,$year),($format?"%m":"%b"));
- $month=dol_substr($month,0,3);
- $data[$i-1] = array($month, $res[$i]);
- }
- return $data;
- }
- /**
- * Return number or total of product refs
- *
- * @param string $sql SQL
- * @param int $limit Limit
- * @return array
- */
- function _getAllByProduct($sql, $limit=10)
- {
- global $langs;
- $result=array();
- $res=array();
- dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
- $resql=$this->db->query($sql);
- if ($resql)
- {
- $num = $this->db->num_rows($resql);
- $i = 0; $other=0;
- while ($i < $num)
- {
- $row = $this->db->fetch_row($resql);
- if ($i < $limit || $num == $limit) $result[$i] = array($row[0],$row[1]); // Ref of product, nb
- else $other += $row[1];
- $i++;
- }
- if ($num > $limit) $result[$i] = array($langs->transnoentitiesnoconv("Other"),$other);
- $this->db->free($resql);
- }
- else dol_print_error($this->db);
- return $result;
- }
- }
|