stats.class.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. <?php
  2. /* Copyright (C) 2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
  3. * Copyright (c) 2008-2013 Laurent Destailleur <eldy@users.sourceforge.net>
  4. * Copyright (C) 2012 Regis Houssin <regis.houssin@capnetworks.com>
  5. * Copyright (C) 2012 Marcos García <marcosgdf@gmail.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. /**
  21. * \file htdocs/core/class/stats.class.php
  22. * \ingroup core
  23. * \brief Common class to manage statistics reports
  24. */
  25. /**
  26. * Parent class of statistics class
  27. */
  28. abstract class Stats
  29. {
  30. protected $db;
  31. var $_lastfetchdate=array(); // Dates of cache file read by methods
  32. var $cachefilesuffix=''; // Suffix to add to name of cache file (to avoid file name conflicts)
  33. /**
  34. * Return nb of elements by month for several years
  35. *
  36. * @param int $endyear Start year
  37. * @param int $startyear End year
  38. * @param int $cachedelay Delay we accept for cache file (0=No read, no save of cache, -1=No read but save)
  39. * @return array Array of values
  40. */
  41. function getNbByMonthWithPrevYear($endyear,$startyear,$cachedelay=0)
  42. {
  43. global $conf,$user,$langs;
  44. if ($startyear > $endyear) return -1;
  45. $datay=array();
  46. // Search into cache
  47. if (! empty($cachedelay))
  48. {
  49. include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  50. include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
  51. }
  52. $newpathofdestfile=$conf->user->dir_temp.'/'.get_class($this).'_'.__FUNCTION__.'_'.(empty($this->cachefilesuffix)?'':$this->cachefilesuffix.'_').$langs->defaultlang.'_entity.'.$conf->entity.'_user'.$user->id.'.cache';
  53. $newmask='0644';
  54. $nowgmt = dol_now();
  55. $foundintocache=0;
  56. if ($cachedelay > 0)
  57. {
  58. $filedate=dol_filemtime($newpathofdestfile);
  59. if ($filedate >= ($nowgmt - $cachedelay))
  60. {
  61. $foundintocache=1;
  62. $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$filedate;
  63. }
  64. else
  65. {
  66. 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.");
  67. }
  68. }
  69. // Load file into $data
  70. if ($foundintocache) // Cache file found and is not too old
  71. {
  72. dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate.".");
  73. $data = json_decode(file_get_contents($newpathofdestfile), true);
  74. }
  75. else
  76. {
  77. $year=$startyear;
  78. while ($year <= $endyear)
  79. {
  80. $datay[$year] = $this->getNbByMonth($year);
  81. $year++;
  82. }
  83. $data = array();
  84. for ($i = 0 ; $i < 12 ; $i++)
  85. {
  86. $data[$i][]=$datay[$endyear][$i][0];
  87. $year=$startyear;
  88. while($year <= $endyear)
  89. {
  90. $data[$i][]=$datay[$year][$i][1];
  91. $year++;
  92. }
  93. }
  94. }
  95. // Save cache file
  96. if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1))
  97. {
  98. dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk.");
  99. if (! dol_is_dir($conf->user->dir_temp)) dol_mkdir($conf->user->dir_temp);
  100. $fp = fopen($newpathofdestfile, 'w');
  101. fwrite($fp, json_encode($data));
  102. fclose($fp);
  103. if (! empty($conf->global->MAIN_UMASK)) $newmask=$conf->global->MAIN_UMASK;
  104. @chmod($newpathofdestfile, octdec($newmask));
  105. $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$nowgmt;
  106. }
  107. // return array(array('Month',val1,val2,val3),...)
  108. return $data;
  109. }
  110. /**
  111. * Return amount of elements by month for several years.
  112. * Criterias used to build request are defined into the constructor of parent class into xxx/class/xxxstats.class.php
  113. * The caller of class can add more filters into sql request by adding criteris into the $stats->where property just after
  114. * calling constructor.
  115. *
  116. * @param int $endyear Start year
  117. * @param int $startyear End year
  118. * @param int $cachedelay Delay we accept for cache file (0=No read, no save of cache, -1=No read but save)
  119. * @return array Array of values
  120. */
  121. function getAmountByMonthWithPrevYear($endyear,$startyear,$cachedelay=0)
  122. {
  123. global $conf,$user,$langs;
  124. if ($startyear > $endyear) return -1;
  125. $datay=array();
  126. // Search into cache
  127. if (! empty($cachedelay))
  128. {
  129. include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  130. include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
  131. }
  132. $newpathofdestfile=$conf->user->dir_temp.'/'.get_class($this).'_'.__FUNCTION__.'_'.(empty($this->cachefilesuffix)?'':$this->cachefilesuffix.'_').$langs->defaultlang.'_entity.'.$conf->entity.'_user'.$user->id.'.cache';
  133. $newmask='0644';
  134. $nowgmt = dol_now();
  135. $foundintocache=0;
  136. if ($cachedelay > 0)
  137. {
  138. $filedate=dol_filemtime($newpathofdestfile);
  139. if ($filedate >= ($nowgmt - $cachedelay))
  140. {
  141. $foundintocache=1;
  142. $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$filedate;
  143. }
  144. else
  145. {
  146. 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.");
  147. }
  148. }
  149. // Load file into $data
  150. if ($foundintocache) // Cache file found and is not too old
  151. {
  152. dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate.".");
  153. $data = json_decode(file_get_contents($newpathofdestfile), true);
  154. }
  155. else
  156. {
  157. $year=$startyear;
  158. while($year <= $endyear)
  159. {
  160. $datay[$year] = $this->getAmountByMonth($year);
  161. $year++;
  162. }
  163. $data = array();
  164. // $data = array('xval'=>array(0=>xlabel,1=>yval1,2=>yval2...),...)
  165. for ($i = 0 ; $i < 12 ; $i++)
  166. {
  167. $data[$i][]=$datay[$endyear][$i][0]; // set label
  168. $year=$startyear;
  169. while($year <= $endyear)
  170. {
  171. $data[$i][]=$datay[$year][$i][1]; // set yval for x=i
  172. $year++;
  173. }
  174. }
  175. }
  176. // Save cache file
  177. if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1))
  178. {
  179. dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk.");
  180. if (! dol_is_dir($conf->user->dir_temp)) dol_mkdir($conf->user->dir_temp);
  181. $fp = fopen($newpathofdestfile, 'w');
  182. if ($fp)
  183. {
  184. fwrite($fp, json_encode($data));
  185. fclose($fp);
  186. if (! empty($conf->global->MAIN_UMASK)) $newmask=$conf->global->MAIN_UMASK;
  187. @chmod($newpathofdestfile, octdec($newmask));
  188. }
  189. else dol_syslog("Failed to write cache file", LOG_ERR);
  190. $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$nowgmt;
  191. }
  192. return $data;
  193. }
  194. /**
  195. * Return average of entity by month for several years
  196. *
  197. * @param int $endyear Start year
  198. * @param int $startyear End year
  199. * @return array Array of values
  200. */
  201. function getAverageByMonthWithPrevYear($endyear,$startyear)
  202. {
  203. if ($startyear > $endyear) return -1;
  204. $datay=array();
  205. $year=$startyear;
  206. while($year <= $endyear)
  207. {
  208. $datay[$year] = $this->getAverageByMonth($year);
  209. $year++;
  210. }
  211. $data = array();
  212. for ($i = 0 ; $i < 12 ; $i++)
  213. {
  214. $data[$i][]=$datay[$endyear][$i][0];
  215. $year=$startyear;
  216. while($year <= $endyear)
  217. {
  218. $data[$i][]=$datay[$year][$i][1];
  219. $year++;
  220. }
  221. }
  222. return $data;
  223. }
  224. /**
  225. * Return count, and sum of products
  226. *
  227. * @param int $year Year
  228. * @param int $cachedelay Delay we accept for cache file (0=No read, no save of cache, -1=No read but save)
  229. * @return array Array of values
  230. */
  231. function getAllByProductEntry($year,$cachedelay=0)
  232. {
  233. global $conf,$user,$langs;
  234. $datay=array();
  235. // Search into cache
  236. if (! empty($cachedelay))
  237. {
  238. include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  239. include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
  240. }
  241. $newpathofdestfile=$conf->user->dir_temp.'/'.get_class($this).'_'.__FUNCTION__.'_'.(empty($this->cachefilesuffix)?'':$this->cachefilesuffix.'_').$langs->defaultlang.'_entity.'.$conf->entity.'_user'.$user->id.'.cache';
  242. $newmask='0644';
  243. $nowgmt = dol_now();
  244. $foundintocache=0;
  245. if ($cachedelay > 0)
  246. {
  247. $filedate=dol_filemtime($newpathofdestfile);
  248. if ($filedate >= ($nowgmt - $cachedelay))
  249. {
  250. $foundintocache=1;
  251. $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$filedate;
  252. }
  253. else
  254. {
  255. 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.");
  256. }
  257. }
  258. // Load file into $data
  259. if ($foundintocache) // Cache file found and is not too old
  260. {
  261. dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate.".");
  262. $data = json_decode(file_get_contents($newpathofdestfile), true);
  263. }
  264. else
  265. {
  266. $data=$this->getAllByProduct($year);
  267. // $data[$i][]=$datay[$year][$i][1]; // set yval for x=i
  268. }
  269. // Save cache file
  270. if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1))
  271. {
  272. dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk.");
  273. if (! dol_is_dir($conf->user->dir_temp)) dol_mkdir($conf->user->dir_temp);
  274. $fp = fopen($newpathofdestfile, 'w');
  275. if ($fp)
  276. {
  277. fwrite($fp, json_encode($data));
  278. fclose($fp);
  279. if (! empty($conf->global->MAIN_UMASK)) $newmask=$conf->global->MAIN_UMASK;
  280. @chmod($newpathofdestfile, octdec($newmask));
  281. }
  282. $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$nowgmt;
  283. }
  284. return $data;
  285. }
  286. // Here we have low level of shared code called by XxxStats.class.php
  287. /**
  288. * Return nb of elements by year
  289. *
  290. * @param string $sql SQL request
  291. * @return array
  292. */
  293. function _getNbByYear($sql)
  294. {
  295. $result = array();
  296. dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
  297. $resql=$this->db->query($sql);
  298. if ($resql)
  299. {
  300. $num = $this->db->num_rows($resql);
  301. $i = 0;
  302. while ($i < $num)
  303. {
  304. $row = $this->db->fetch_row($resql);
  305. $result[$i] = $row;
  306. $i++;
  307. }
  308. $this->db->free($resql);
  309. }
  310. else {
  311. dol_print_error($this->db);
  312. }
  313. return $result;
  314. }
  315. /**
  316. * Return nb of elements, total amount and avg amount each year
  317. *
  318. * @param string $sql SQL request
  319. * @return array Array with nb, total amount, average for each year
  320. */
  321. function _getAllByYear($sql)
  322. {
  323. $result = array();
  324. dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
  325. $resql=$this->db->query($sql);
  326. if ($resql)
  327. {
  328. $num = $this->db->num_rows($resql);
  329. $i = 0;
  330. while ($i < $num)
  331. {
  332. $row = $this->db->fetch_object($resql);
  333. $result[$i]['year'] = $row->year;
  334. $result[$i]['nb'] = $row->nb;
  335. if($i>0 && $row->nb) $result[$i-1]['nb_diff'] = ($result[$i-1]['nb'] - $row->nb) / $row->nb * 100;
  336. $result[$i]['total'] = $row->total;
  337. if($i>0 && $row->total) $result[$i-1]['total_diff'] = ($result[$i-1]['total'] - $row->total) / $row->total * 100;
  338. $result[$i]['avg'] = $row->avg;
  339. if($i>0 && $row->avg) $result[$i-1]['avg_diff'] = ($result[$i-1]['avg'] - $row->avg) / $row->avg * 100;
  340. // For some $sql only
  341. if (isset($row->weighted))
  342. {
  343. $result[$i]['weighted'] = $row->weighted;
  344. if($i>0 && $row->weighted) $result[$i-1]['avg_weighted'] = ($result[$i-1]['weighted'] - $row->weighted) / $row->weighted * 100;
  345. }
  346. $i++;
  347. }
  348. $this->db->free($resql);
  349. }
  350. else {
  351. dol_print_error($this->db);
  352. }
  353. return $result;
  354. }
  355. /**
  356. * Renvoie le nombre de proposition par mois pour une annee donnee
  357. *
  358. * @param int $year Year
  359. * @param string $sql SQL
  360. * @param int $format 0=Label of absiss is a translated text, 1=Label of absiss is a number
  361. * @return array Array of nb each month
  362. */
  363. function _getNbByMonth($year, $sql, $format=0)
  364. {
  365. $result=array();
  366. $res=array();
  367. dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
  368. $resql=$this->db->query($sql);
  369. if ($resql)
  370. {
  371. $num = $this->db->num_rows($resql);
  372. $i = 0; $j = 0;
  373. while ($i < $num)
  374. {
  375. $row = $this->db->fetch_row($resql);
  376. $j = $row[0] * 1;
  377. $result[$j] = $row[1];
  378. $i++;
  379. }
  380. $this->db->free($resql);
  381. }
  382. else
  383. {
  384. dol_print_error($this->db);
  385. }
  386. for ($i = 1 ; $i < 13 ; $i++)
  387. {
  388. $res[$i] = (isset($result[$i])?$result[$i]:0);
  389. }
  390. $data = array();
  391. for ($i = 1 ; $i < 13 ; $i++)
  392. {
  393. $month=dol_print_date(dol_mktime(12,0,0,$i,1,$year),($format?"%m":"%b"));
  394. $month=dol_substr($month,0,3);
  395. $data[$i-1] = array($month, $res[$i]);
  396. }
  397. return $data;
  398. }
  399. /**
  400. * Renvoie le nombre d'element par mois pour une annee donnee
  401. *
  402. * @param int $year Year
  403. * @param string $sql SQL
  404. * @param int $format 0=Label of absiss is a translated text, 1=Label of absiss is a number
  405. * @return array
  406. */
  407. function _getAmountByMonth($year, $sql, $format=0)
  408. {
  409. $result=array();
  410. $res=array();
  411. dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
  412. $resql=$this->db->query($sql);
  413. if ($resql)
  414. {
  415. $num = $this->db->num_rows($resql);
  416. $i = 0;
  417. while ($i < $num)
  418. {
  419. $row = $this->db->fetch_row($resql);
  420. $j = $row[0] * 1;
  421. $result[$j] = $row[1];
  422. $i++;
  423. }
  424. $this->db->free($resql);
  425. }
  426. else dol_print_error($this->db);
  427. for ($i = 1 ; $i < 13 ; $i++)
  428. {
  429. $res[$i] = (int) round((isset($result[$i])?$result[$i]:0));
  430. }
  431. $data = array();
  432. for ($i = 1 ; $i < 13 ; $i++)
  433. {
  434. $month=dol_print_date(dol_mktime(12,0,0,$i,1,$year),($format?"%m":"%b"));
  435. $month=dol_substr($month,0,3);
  436. $data[$i-1] = array($month, $res[$i]);
  437. }
  438. return $data;
  439. }
  440. /**
  441. * Renvoie le montant moyen par mois pour une annee donnee
  442. *
  443. * @param int $year Year
  444. * @param string $sql SQL
  445. * @param int $format 0=Label of absiss is a translated text, 1=Label of absiss is a number
  446. * @return array
  447. */
  448. function _getAverageByMonth($year, $sql, $format=0)
  449. {
  450. $result=array();
  451. $res=array();
  452. dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
  453. $resql=$this->db->query($sql);
  454. if ($resql)
  455. {
  456. $num = $this->db->num_rows($resql);
  457. $i = 0; $j = 0;
  458. while ($i < $num)
  459. {
  460. $row = $this->db->fetch_row($resql);
  461. $j = $row[0] * 1;
  462. $result[$j] = $row[1];
  463. $i++;
  464. }
  465. $this->db->free($resql);
  466. }
  467. else dol_print_error($this->db);
  468. for ($i = 1 ; $i < 13 ; $i++)
  469. {
  470. $res[$i] = (isset($result[$i])?$result[$i]:0);
  471. }
  472. $data = array();
  473. for ($i = 1 ; $i < 13 ; $i++)
  474. {
  475. $month=dol_print_date(dol_mktime(12,0,0,$i,1,$year),($format?"%m":"%b"));
  476. $month=dol_substr($month,0,3);
  477. $data[$i-1] = array($month, $res[$i]);
  478. }
  479. return $data;
  480. }
  481. /**
  482. * Return number or total of product refs
  483. *
  484. * @param string $sql SQL
  485. * @param int $limit Limit
  486. * @return array
  487. */
  488. function _getAllByProduct($sql, $limit=10)
  489. {
  490. global $langs;
  491. $result=array();
  492. $res=array();
  493. dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
  494. $resql=$this->db->query($sql);
  495. if ($resql)
  496. {
  497. $num = $this->db->num_rows($resql);
  498. $i = 0; $other=0;
  499. while ($i < $num)
  500. {
  501. $row = $this->db->fetch_row($resql);
  502. if ($i < $limit || $num == $limit) $result[$i] = array($row[0],$row[1]); // Ref of product, nb
  503. else $other += $row[1];
  504. $i++;
  505. }
  506. if ($num > $limit) $result[$i] = array($langs->transnoentitiesnoconv("Other"),$other);
  507. $this->db->free($resql);
  508. }
  509. else dol_print_error($this->db);
  510. return $result;
  511. }
  512. }