PageRenderTime 57ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/application/models/stats.php

https://github.com/Magaz/Ushahidi_Web
PHP | 569 lines | 394 code | 96 blank | 79 comment | 42 complexity | bcc1660e3666499fac3ef2110261724c MD5 | raw file
Possible License(s): AGPL-1.0, LGPL-3.0, BSD-3-Clause, GPL-2.0, LGPL-2.1
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Model for Statistics
  4. *
  5. *
  6. * PHP version 5
  7. * LICENSE: This source file is subject to LGPL license
  8. * that is available through the world-wide-web at the following URI:
  9. * http://www.gnu.org/copyleft/lesser.html
  10. * @author Ushahidi Team <team@ushahidi.com>
  11. * @package Ushahidi - http://source.ushahididev.com
  12. * @subpackage Models
  13. * @copyright Ushahidi - http://www.ushahidi.com
  14. * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License (LGPL)
  15. */
  16. class Stats_Model extends ORM {
  17. static $time_out = 1;
  18. /**
  19. * Generates the JavaScript for stats tracking
  20. */
  21. public static function get_javascript()
  22. {
  23. // Make sure cURL is installed
  24. if ( ! function_exists('curl_exec'))
  25. {
  26. throw new Kohana_Exception('footer.cURL_not_installed');
  27. return false;
  28. }
  29. // Get the stat id
  30. $stat_id = Settings_Model::get_setting('stat_id');
  31. // If stats isn't set, ignore this
  32. if ($stat_id == 0)
  33. return '';
  34. $cache = Cache::instance();
  35. $tag = $cache->get(Kohana::config('settings.subdomain').'_piwiktag');
  36. if ( ! $tag)
  37. { // Cache is Empty so Re-Cache
  38. // Grabbing the URL to update stats URL, Name, Reports, etc on the stats server
  39. $additional_query = '';
  40. if (isset($_SERVER["HTTP_HOST"]))
  41. {
  42. // Grab the site domain from the config and trim any whitespaces
  43. $site_domain = trim(Kohana::config('config.site_domain'));
  44. $slashornoslash = '';
  45. if (empty($site_domain) OR $site_domain{0} != '/')
  46. {
  47. $slashornoslash = '/';
  48. }
  49. // URL
  50. $val = 'http://'.$_SERVER["HTTP_HOST"].$slashornoslash.$site_domain;
  51. $additional_query = '&val='.base64_encode($val);
  52. // Site Name
  53. $site_name = utf8tohtml::convert(Kohana::config('settings.site_name'),TRUE);
  54. $additional_query .= '&sitename='.base64_encode($site_name);
  55. // Version
  56. $version = Kohana::config('settings.ushahidi_version');
  57. $additional_query .= '&version='.base64_encode($version);
  58. // Report Count
  59. $number_reports = ORM::factory("incident")->where("incident_active", 1)->count_all();
  60. $additional_query .= '&reports='.base64_encode($number_reports);
  61. // Latitude
  62. $latitude = Kohana::config('settings.default_lat');
  63. $additional_query .= '&lat='.base64_encode($latitude);
  64. // Longitude
  65. $longitude = Kohana::config('settings.default_lon');
  66. $additional_query .= '&lon='.base64_encode($longitude);
  67. }
  68. $url = 'https://tracker.ushahidi.com/dev.px.php?task=tc&siteid='.$stat_id.$additional_query;
  69. $curl_handle = curl_init();
  70. // cURL options
  71. $curl_options = array(
  72. CURLOPT_URL => $url,
  73. // Timeout set to 15 seconds. This is somewhat arbitrary and can be changed.
  74. CURLOPT_CONNECTTIMEOUT => self::$time_out,
  75. // Set cURL to store data in variable instead of print
  76. CURLOPT_RETURNTRANSFER => 1,
  77. CURLOPT_SSL_VERIFYPEER => FALSE
  78. );
  79. curl_setopt_array($curl_handle, $curl_options);
  80. $buffer = curl_exec($curl_handle);
  81. curl_close($curl_handle);
  82. try
  83. {
  84. // This works because the tracking code is only wrapped in one tag
  85. $tag = (string) @simplexml_load_string($buffer);
  86. }
  87. catch (Exception $e)
  88. {
  89. // In case the xml was malformed for whatever reason, we will just guess what the tag should be here
  90. $tag = <<< STATSCOLLECTOR
  91. <!-- Stats Collector -->
  92. <script type="text/javascript">
  93. setTimeout(function() {
  94. var statsCollector = document.createElement('img');
  95. statsCollector.src = document.location.protocol + "//tracker.ushahidi.com/piwik/piwik.php?idsite={$stat_id}&rec=1";
  96. statsCollector.style.cssText = "width: 1px; height: 1px; opacity: 0.1;";
  97. document.body.appendChild(statsCollector);
  98. }, 100);
  99. </script>
  100. <!-- End Stats Collector -->
  101. STATSCOLLECTOR;
  102. }
  103. // Reset Cache Here
  104. $cache->set(Kohana::config('settings.subdomain').'_piwiktag', $tag, array('piwiktag'), 86400); // 1 Day
  105. }
  106. return $tag;
  107. }
  108. /*
  109. * range will be ignored if dp1 and dp2 are set
  110. * dp1 and dp2 format is YYYY-MM-DD
  111. */
  112. public static function get_hit_stats($range=30, $dp1=NULL, $dp2=NULL)
  113. {
  114. // Get ID for stats
  115. $stat_id = Settings_Model::get_setting('stat_id');
  116. $stat_key = Settings_Model::get_setting('stat_key');
  117. $twodates = '';
  118. if ($dp1 !== NULL AND $dp2 !== NULL)
  119. {
  120. $twodates = '&twodates='.urlencode($dp1.','.$dp2);
  121. }
  122. $stat_url = 'https://tracker.ushahidi.com/px.php?stat_key='.$stat_key
  123. .'&task=stats&siteid='.urlencode($stat_id).'&period=day&range='.urlencode($range).$twodates;
  124. // Ignore errors since we are error checking later
  125. $response = @simplexml_load_string(self::_curl_req($stat_url));
  126. // If we encounter an error, return false
  127. if
  128. (
  129. isset($response->result->error[0]) OR
  130. isset($response->error[0]) OR
  131. ! isset($response->visits->result)
  132. )
  133. {
  134. Kohana::log('error', "Error on stats request");
  135. return false;
  136. }
  137. foreach ($response->visits->result as $res)
  138. {
  139. $dt = $res['date'];
  140. $y = substr($dt,0,4);
  141. $m = substr($dt,5,2);
  142. $d = substr($dt,8,2);
  143. $timestamp = mktime(0,0,0,$m,$d,$y)*1000;
  144. if (isset($res->nb_visits))
  145. {
  146. $data['visits'][ (string) $timestamp] = (string) $res->nb_visits;
  147. }
  148. else
  149. {
  150. $data['visits'][ (string) $timestamp] = '0';
  151. }
  152. if (isset($res->nb_uniq_visitors))
  153. {
  154. $data['uniques'][ (string) $timestamp] = (string) $res->nb_uniq_visitors;
  155. }
  156. else
  157. {
  158. $data['uniques'][ (string) $timestamp] = '0';
  159. }
  160. if (isset($res->nb_actions))
  161. {
  162. $data['pageviews'][ (string) $timestamp] = (string) $res->nb_actions;
  163. }
  164. else
  165. {
  166. $data['pageviews'][ (string) $timestamp] = '0';
  167. }
  168. }
  169. return $data;
  170. }
  171. static function get_hit_countries($range=30, $dp1=NULL, $dp2=NULL)
  172. {
  173. $stat_id = Settings_Model::get_setting('stat_id');
  174. $stat_key = Settings_Model::get_setting('stat_key');
  175. $twodates = '';
  176. if ($dp1 !== NULL AND $dp2 !== NULL)
  177. {
  178. $twodates = '&twodates='.urlencode($dp1.','.$dp2);
  179. }
  180. $stat_url = 'https://tracker.ushahidi.com/px.php?stat_key='.$stat_key
  181. .'&task=stats&siteid='.urlencode($stat_id).'&period=day&range='.urlencode($range).$twodates;
  182. // Ignore errors since we are error checking later
  183. $response = @simplexml_load_string(self::_curl_req($stat_url));
  184. // If we encounter an error, return false
  185. if
  186. (
  187. isset($response->result->error[0]) OR
  188. isset($response->error[0]) OR
  189. ! isset($response->countries->result)
  190. )
  191. {
  192. Kohana::log('error', "Error on stats request");
  193. return false;
  194. }
  195. $data = array();
  196. foreach ($response->countries->result as $res)
  197. {
  198. $date = (string) $res['date'];
  199. foreach ($res->row as $row)
  200. {
  201. $code = (string) $row->code;
  202. $data[$date][$code]['label'] = (string) $row->label;
  203. $data[$date][$code]['uniques'] = (string) $row->nb_uniq_visitors;
  204. $logo = (string) $row->logo;
  205. $data[$date][$code]['logo'] = 'https://tracker.ushahidi.com/piwik/'.$logo;
  206. }
  207. }
  208. return $data;
  209. }
  210. /*
  211. * get an array of report counts
  212. * @param approved - Only count approved reports if true
  213. * @param by_time - Format array with timestamp as the key if true
  214. * @param range - Number of days back from today to pull reports from. Will end up defaulting to 100000 days to get them all.
  215. * @param dp1 - Arbitrary date range. Low date. YYYY-MM-DD
  216. * @param dp2 - Arbitrary date range. High date. YYYY-MM-DD
  217. */
  218. static function get_report_stats($approved=FALSE, $by_time=FALSE, $range=NULL, $dp1=NULL, $dp2=NULL, $line_chart_data=FALSE)
  219. {
  220. if ($range === NULL)
  221. {
  222. $range = 100000;
  223. }
  224. if ($dp1 === NULL)
  225. {
  226. $dp1 = 0;
  227. }
  228. if ($dp2 === NULL)
  229. {
  230. $dp2 = '3000-01-01';
  231. }
  232. // Set up the range calculation
  233. $time = time() - ($range*86400);
  234. $range_date = date('Y-m-d', $time);
  235. // Only grab approved
  236. if ($approved)
  237. {
  238. $reports = ORM::factory('incident')
  239. ->where('incident_active', '1')
  240. ->where('incident_date >=', $dp1)
  241. ->where('incident_date <=',$dp2)
  242. ->where('incident_date >', $range_date)
  243. ->find_all();
  244. }
  245. else
  246. {
  247. $reports = ORM::factory('incident')
  248. ->where('incident_date >=', $dp1)
  249. ->where('incident_date <=', $dp2)
  250. ->where('incident_date >', $range_date)
  251. ->find_all();
  252. }
  253. $reports_categories = ORM::factory('incident_category')->find_all();
  254. // Initialize arrays so we don't error out
  255. $report_data = array();
  256. $verified_counts = array();
  257. $approved_counts = array();
  258. $all = array();
  259. $earliest_timestamp = 32503680000; // Year 3000 in epoch so we can catch everything less than this.
  260. $latest_timestamp = 0;
  261. // Gather some data into an array on incident reports
  262. $num_reports = 0;
  263. foreach ($reports as $report)
  264. {
  265. $timestamp = (string) strtotime(substr($report->incident_date,0,10));
  266. $report_data[$report->id] = array(
  267. 'date'=>$timestamp,
  268. 'mode'=>$report->incident_mode,
  269. 'active'=>$report->incident_active,
  270. 'verified'=>$report->incident_verified
  271. );
  272. if ($timestamp < $earliest_timestamp)
  273. {
  274. $earliest_timestamp = $timestamp;
  275. }
  276. if ($timestamp > $latest_timestamp)
  277. {
  278. $latest_timestamp = $timestamp;
  279. }
  280. if ( ! isset($verified_counts['verified'][$timestamp]))
  281. {
  282. $verified_counts['verified'][$timestamp] = 0;
  283. $verified_counts['unverified'][$timestamp] = 0;
  284. $approved_counts['approved'][$timestamp] = 0;
  285. $approved_counts['unapproved'][$timestamp] = 0;
  286. $all[$timestamp] = 0;
  287. }
  288. $all[$timestamp]++;
  289. if ($report->incident_verified == 1)
  290. {
  291. $verified_counts['verified'][$timestamp]++;
  292. }
  293. else
  294. {
  295. $verified_counts['unverified'][$timestamp]++;
  296. }
  297. if ($report->incident_active == 1)
  298. {
  299. $approved_counts['approved'][$timestamp]++;
  300. }
  301. else
  302. {
  303. $approved_counts['unapproved'][$timestamp]++;
  304. }
  305. $num_reports++;
  306. }
  307. $category_counts = array();
  308. $lowest_date = 9999999999; // Really far in the future.
  309. $highest_date = 0;
  310. foreach ($reports_categories as $report)
  311. {
  312. // If this report category doesn't have any reports (in case we are only
  313. // looking at approved reports), move on to the next one.
  314. if ( ! isset($report_data[$report->incident_id]))
  315. continue;
  316. $c_id = $report->category_id;
  317. $timestamp = $report_data[$report->incident_id]['date'];
  318. if ($timestamp < $lowest_date)
  319. {
  320. $lowest_date = $timestamp;
  321. }
  322. if ($timestamp > $highest_date)
  323. {
  324. $highest_date = $timestamp;
  325. }
  326. if ( ! isset($category_counts[$c_id][$timestamp]))
  327. {
  328. $category_counts[$c_id][$timestamp] = 0;
  329. }
  330. $category_counts[$c_id][$timestamp]++;
  331. }
  332. // Populate date range
  333. $date_range = array();
  334. $add_date = $lowest_date;
  335. while ($add_date <= $highest_date)
  336. {
  337. $date_range[] = $add_date;
  338. $add_date += 86400;
  339. }
  340. // Zero out days that don't have a count
  341. foreach ($category_counts as & $arr)
  342. {
  343. foreach ($date_range as $timestamp)
  344. {
  345. if ( ! isset($arr[$timestamp]))
  346. {
  347. $arr[$timestamp] = 0;
  348. }
  349. if ( ! isset($verified_counts['verified'][$timestamp]))
  350. {
  351. $verified_counts['verified'][$timestamp] = 0;
  352. }
  353. if ( ! isset($verified_counts['unverified'][$timestamp]))
  354. {
  355. $verified_counts['unverified'][$timestamp] = 0;
  356. }
  357. if ( ! isset($approved_counts['approved'][$timestamp]))
  358. {
  359. $approved_counts['approved'][$timestamp] = 0;
  360. }
  361. if ( ! isset($approved_counts['unapproved'][$timestamp]))
  362. {
  363. $approved_counts['unapproved'][$timestamp] = 0;
  364. }
  365. if ( ! isset($all[$timestamp]))
  366. {
  367. $all[$timestamp] = 0;
  368. }
  369. }
  370. // keep dates in order
  371. ksort($arr);
  372. ksort($verified_counts['verified']);
  373. ksort($verified_counts['unverified']);
  374. ksort($approved_counts['approved']);
  375. ksort($approved_counts['unapproved']);
  376. ksort($all);
  377. }
  378. // Add all our data sets to the array we are returning
  379. $data['category_counts'] = $category_counts;
  380. $data['verified_counts'] = $verified_counts;
  381. $data['approved_counts'] = $approved_counts;
  382. $data['all']['all'] = $all;
  383. // I'm just tacking this on here. However, we could improve performance
  384. // by implementing the code above but I just don't have the time
  385. // to mess with it.
  386. if ($by_time)
  387. {
  388. // Reorder the array. Is there a built in PHP function that can do this?
  389. $new_data = array();
  390. foreach ($data as $main_key => $data_array)
  391. {
  392. foreach ($data_array as $key => $counts)
  393. {
  394. if ($line_chart_data == FALSE)
  395. {
  396. foreach ($counts as $timestamp => $count)
  397. {
  398. $new_data[$main_key][$timestamp][$key] = $count;
  399. }
  400. }
  401. else
  402. {
  403. foreach ($counts as $timestamp => $count)
  404. {
  405. $timestamp_key = (string) ($timestamp*1000);
  406. if ( ! isset($new_data[$main_key][$timestamp_key]))
  407. {
  408. $new_data[$main_key][$timestamp_key] = 0;
  409. }
  410. $new_data[$main_key][$timestamp_key] += $count;
  411. }
  412. }
  413. }
  414. }
  415. $data = $new_data;
  416. }
  417. if ($line_chart_data == FALSE)
  418. {
  419. $data['total_reports'] = $num_reports;
  420. $data['total_categories'] = count($category_counts);
  421. $data['earliest_report_time'] = $earliest_timestamp;
  422. $data['latest_report_time'] = $latest_timestamp;
  423. }
  424. return $data;
  425. }
  426. /**
  427. * Creates a new site in centralized stat tracker
  428. * @param sitename - name of the instance
  429. * @param url - base url
  430. */
  431. public function create_site( $sitename, $url)
  432. {
  433. $stat_url = 'https://tracker.ushahidi.com/px.php?task=cs&sitename='.urlencode($sitename).'&url='.urlencode($url);
  434. // Ignore errors since we are error checking later
  435. $xml = simplexml_load_string(Stats_Model::_curl_req($stat_url));
  436. $stat_id = (string) $xml->id[0];
  437. $stat_key = (string) $xml->key[0];
  438. if ($stat_id > 0)
  439. {
  440. Settings_Model::save_setting('stat_id', $stat_id);
  441. Settings_Model::save_setting('stat_key', $stat_key);
  442. return $stat_id;
  443. }
  444. return false;
  445. }
  446. /**
  447. * Helper function to send a cURL request
  448. * @param url - URL for cURL to hit
  449. */
  450. public function _curl_req($url)
  451. {
  452. // Make sure cURL is installed
  453. if ( ! function_exists('curl_exec'))
  454. {
  455. throw new Kohana_Exception('stats.cURL_not_installed');
  456. return false;
  457. }
  458. $curl_handle = curl_init();
  459. // cURL options
  460. $curl_options = array(
  461. CURLOPT_URL => $url,
  462. // Timeout set to 15 seconds. This is somewhat arbitrary and can be changed.
  463. CURLOPT_CONNECTTIMEOUT => 15,
  464. // Set curl to store data in variable instead of print
  465. CURLOPT_RETURNTRANSFER => 1,
  466. CURLOPT_SSL_VERIFYPEER => FALSE
  467. );
  468. curl_setopt_array($curl_handle, $curl_options);
  469. $buffer = curl_exec($curl_handle);
  470. curl_close($curl_handle);
  471. return $buffer;
  472. }
  473. }