PageRenderTime 67ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/system/application/controllers/tracker.php

https://github.com/kylecronin/sof-update
PHP | 589 lines | 372 code | 147 blank | 70 comment | 51 complexity | 9c95bd4ec4465bc677930fc9a607c6d4 MD5 | raw file
  1. <?php
  2. class Tracker extends Controller {
  3. function index()
  4. {
  5. $this->load->view('tracker_index');
  6. }
  7. function chart($user, $siteid = 1, $low = false, $high = false)
  8. {
  9. if (file_exists("sof.db-journal"))
  10. unlink("sof.db-journal");
  11. $this->load->database();
  12. if (!$low)
  13. $low = "month";
  14. if (!$high)
  15. $high = time();
  16. if (!strcmp($low, "all"))
  17. $low = 0;
  18. if (!strcmp($low, "year"))
  19. $low = time()-31557600;
  20. if (!strcmp($low, "month"))
  21. $low = time()-2592000;
  22. if (!strcmp($low, "week"))
  23. $low = time()-604800;
  24. if (!strcmp($low, "day"))
  25. $low = time()-86400;
  26. if (!strcmp($low, "hour"))
  27. $low = time()-3600;
  28. $query = $this->db->query("SELECT rep, questions, answers, date FROM profile WHERE user = '$user' AND site = '$siteid' AND $low < date AND date < $high ORDER BY date ASC");
  29. $data = "";
  30. foreach ($query->result() as $row)
  31. {
  32. $r = $row->rep;
  33. $q = $row->questions;
  34. $a = $row->answers;
  35. $pd = $row->date;
  36. $d = $pd."000";
  37. $data .= "[$d, $r, $q, $a],\n";
  38. }
  39. $data = substr_replace($data, "", -2); // remove last comma
  40. if (file_exists("sof.db-journal"))
  41. unlink("sof.db-journal");
  42. $this->load->database();
  43. $dbitem = $this->db->query("SELECT * FROM sites WHERE id=\"$siteid\"")->row();
  44. $sites = array(1 => array('sitename' => 'StackOverflow'),
  45. 2 => array('sitename' => 'ServerFault'),
  46. 3 => array('sitename' => 'Meta'),
  47. 4 => array('sitename' => 'SuperUser'));
  48. $sitename = $dbitem->name;
  49. $this->load->view('header', compact('sitename'));
  50. $this->load->view('chart', compact('data'));
  51. $this->load->view('footer');
  52. }
  53. function _multifetch($urls)
  54. {
  55. // shhsecret = "Welcome to ServerFault"
  56. $mh = curl_multi_init();
  57. $handles = array();
  58. foreach (array_keys($urls) as $urlkey)
  59. {
  60. $ch = curl_init($urls[$urlkey]);
  61. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  62. //curl_setopt($ch, CURLOPT_HEADER, TRUE);
  63. //curl_setopt($ch, CURLOPT_COOKIE, 'shhsecret="Welcome to SuperUser"');
  64. curl_setopt($ch, CURLOPT_USERAGENT, "http://meta.stackoverflow.com/questions/9863/track-your-reputation");
  65. curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
  66. $handles[$urlkey] = $ch;
  67. curl_multi_add_handle($mh, $ch);
  68. }
  69. do { curl_multi_exec($mh, $active); } while($active > 0);
  70. $output = array();
  71. foreach(array_keys($handles) as $hk)
  72. {
  73. $status = curl_getinfo($handles[$hk], CURLINFO_HTTP_CODE);
  74. if ($status != 200)
  75. $output[$hk] = $status;
  76. else
  77. $output[$hk] = curl_multi_getcontent($handles[$hk]);
  78. }
  79. return $output;
  80. }
  81. function _readstats($source, $siteid)
  82. {
  83. // extract answers from $page, store in $answers (array)
  84. // for an $a in $answers:
  85. // $a[1] => votes
  86. // $a[2] => answer id
  87. // $a[3] => answer text
  88. $areg = '/answer-votes.*?>(-?\d+).*?#(\d+).*?>(.*?)<\/a>( \((\d+)\))?(<\/div>)/s';
  89. preg_match_all($areg, $source, $answers, PREG_SET_ORDER);
  90. $aids = ""; // that's "answer ids"... but try not to catch it
  91. foreach ($answers as $a)
  92. {
  93. if (strcmp($aids, ""))
  94. $aids .= ", ".$a[2];
  95. else
  96. $aids .= $a[2];
  97. }
  98. //echo $aids;
  99. $query = $this->db->query("SELECT id, votes, accepted FROM Questions WHERE id IN ($aids) AND site='$siteid'");
  100. $dbr = array();
  101. foreach ($query->result() as $row)
  102. $dbr[$row->id] = $row;
  103. //print_r($dbr);
  104. $ret = array();
  105. $this->db->query("BEGIN");
  106. foreach($answers as $a)
  107. {
  108. $id = $a[2];
  109. $score = $a[1];
  110. $accepted = preg_match('/answered-accepted" title/s', $a[0]);
  111. $text = $a[3];
  112. $qty = $a[5] ? $a[5] : 1;
  113. //$dbitem = $this->db->query("SELECT votes, accepted FROM Questions WHERE id = '$id'")->row();
  114. if (array_key_exists($id, $dbr))
  115. $dbitem = $dbr[$id];
  116. else
  117. $dbitem = NULL;
  118. if ($dbitem)
  119. {
  120. $new = false;
  121. $oldscore = $dbitem->votes;
  122. $oldacc = $dbitem->accepted;
  123. if (($score-$oldscore) || ($accepted-$oldacc))
  124. $this->db->query("UPDATE Questions SET votes = '$score', accepted = '$accepted' WHERE id = '$id' AND site = '$siteid'");
  125. }
  126. else
  127. {
  128. $text = htmlspecialchars($text, ENT_QUOTES);
  129. $this->db->query("INSERT INTO Questions VALUES('$text', '$score', '$id', '$accepted', '$siteid')");
  130. $new = true;
  131. $oldscore = 0;
  132. $oldacc = 0;
  133. }
  134. array_push($ret,
  135. array( 'id' => $id,
  136. 'new' => $new,
  137. 'newscore' => $score,
  138. 'oldscore' => $oldscore,
  139. 'newacc' => $accepted,
  140. 'oldacc' => $oldacc,
  141. 'qty' => $qty,
  142. 'text' => $text));
  143. }
  144. $this->db->query("END");
  145. //print_r($ret);
  146. return $ret;
  147. }
  148. function _readapijson($source, $user, $siteid)
  149. {
  150. // echo $source;
  151. $areg = '/{"PostUrl":"(\d+)\/?(\d*)#?\d*","PostTitle":"(.*?)","Rep":(-?\d+)}/s';
  152. preg_match_all($areg, $source, $answers, PREG_SET_ORDER);
  153. //print_r($answers);
  154. $ret = array();
  155. $this->db->query("BEGIN");
  156. foreach($answers as $a)
  157. {
  158. $qid = $a[1];
  159. $id = $a[2] ? $a[2] : $qid;
  160. $score = $a[4];
  161. $text = $a[3];
  162. $dbitem = $this->db->query("SELECT rep FROM posts WHERE id = '$id' AND site = '$siteid'")->row();
  163. //echo "query id $id\n";
  164. //print_r($dbitem);
  165. if ($dbitem)
  166. {
  167. $new = false;
  168. $oldscore = $dbitem->rep;
  169. if ($score-$oldscore != 0)
  170. $this->db->query("UPDATE posts SET rep = '$score' WHERE id = '$id' AND site = '$siteid'");
  171. }
  172. else
  173. {
  174. $this->db->query("INSERT INTO posts VALUES('$id', '$qid', '$user', '$score', '$text', '$siteid')");
  175. $new = true;
  176. $oldscore = 0;
  177. }
  178. array_push($ret,
  179. array( 'id' => $id,
  180. 'qid' => $qid,
  181. 'new' => $new,
  182. 'newscore' => $score,
  183. 'oldscore' => $oldscore,
  184. 'text' => $text));
  185. }
  186. $this->db->query("END");
  187. //print_r($ret);
  188. return $ret;
  189. }
  190. function update($user)
  191. {
  192. $this->_update("StackOverflow", "stackoverflow.com", 1, true, $user);
  193. }
  194. function sfupdate($user)
  195. {
  196. $this->_update("ServerFault", "serverfault.com", 2, true, $user);
  197. }
  198. function metaupdate($user)
  199. {
  200. $this->_update("Meta", "meta.stackoverflow.com", 3, true, $user);
  201. }
  202. function suupdate($user)
  203. {
  204. $this->_update("SuperUser", "superuser.com", 4, true, $user);
  205. }
  206. function seupdate($domain, $user)
  207. {
  208. $domain = preg_replace('/_/', '.', $domain);
  209. if (file_exists("sof.db-journal"))
  210. unlink("sof.db-journal");
  211. $this->load->database();
  212. $dbitem = $this->db->query("SELECT * FROM sites WHERE domain=\"$domain\"")->row();
  213. //print_r($dbitem);
  214. if (!$dbitem)
  215. {
  216. $src = file_get_contents("http://$domain/");
  217. preg_match('/<title>(.*?)<\/title>/', $src, $title);
  218. $title = $title[1];
  219. $this->db->query("INSERT INTO sites (name, domain) VALUES (\"$title\", \"$domain\")");
  220. $dbitem = $this->db->query("SELECT * FROM sites WHERE domain=\"$domain\"")->row();
  221. }
  222. //print_r($dbitem);
  223. $this->_update($dbitem->name, $dbitem->domain, $dbitem->id, false, $user);
  224. }
  225. function se2update($domain, $user)
  226. {
  227. $domain = preg_replace('/_/', '.', $domain);
  228. $domain = "$domain.stackexchange.com";
  229. if (file_exists("sof.db-journal"))
  230. unlink("sof.db-journal");
  231. $this->load->database();
  232. $dbitem = $this->db->query("SELECT * FROM sites WHERE domain=\"$domain\"")->row();
  233. //print_r($dbitem);
  234. if (!$dbitem)
  235. {
  236. $src = file_get_contents("http://$domain/");
  237. preg_match('/<title>(.*?)<\/title>/', $src, $title);
  238. $title = $title[1];
  239. $this->db->query("INSERT INTO sites (name, domain) VALUES (\"$title\", \"$domain\")");
  240. $dbitem = $this->db->query("SELECT * FROM sites WHERE domain=\"$domain\"")->row();
  241. }
  242. //print_r($dbitem);
  243. $this->_update($dbitem->name, $dbitem->domain, $dbitem->id, true, $user);
  244. }
  245. function _update($sitename, $site, $siteid, $trilogy, $user)
  246. {
  247. if (file_exists("sof.db-journal"))
  248. unlink("sof.db-journal");
  249. if ($_SERVER['REMOTE_ADDR'] == '8.21.4.254')
  250. {
  251. echo "hello - no human being should see this. if, by chance, you are, let me know http://meta.stackoverflow.com/q/9863/658";
  252. exit(0);
  253. }
  254. $this->load->helper('numformat');
  255. //$this->output->enable_profiler(TRUE);
  256. if (!preg_match('/^\d+$/', $user))
  257. {
  258. $this->load->view('invalid_user', compact('user', 'sitename'));
  259. return;
  260. }
  261. $before = microtime(true);
  262. $data = $this->_multifetch(array('page' => "http://$site/users/$user/",
  263. //'apijson' => "http://stackoverflow.com/users/$user/0/9999999999999"
  264. //'apijson' => "http://$site/users/rep/$user/2000-01-01/2030-01-01",
  265. 'questionsapi' => "http://$site/api/userquestions.html?page=1&pagesize=500&userId=$user&sort=recent",
  266. 'answersapi' => "http://$site/api/useranswers.html?page=1&pagesize=500&userId=$user&sort=recent"
  267. //'test' => "http://modos.org:9999/"
  268. ));
  269. //print_r($data); exit(0);
  270. foreach ($data as $page)
  271. {
  272. if ($page == 404)
  273. {
  274. $this->load->view('invalid_user', compact('user', 'sitename'));
  275. return;
  276. }
  277. }
  278. extract($data);
  279. //echo "that's better";
  280. //
  281. /*print_r($data);
  282. exit(0);*/
  283. $during = microtime(true);
  284. // extract reputation from $page, store in $rep
  285. if ($trilogy)
  286. $exp = '/summarycount">([,\d]+)<\/div>\s*<div style="margin-top:5px; font-weight:bold">reputation/s';
  287. else
  288. $exp = '/summarycount">.*?([,\d]+)<\/div>.*?Reputation/s';
  289. preg_match($exp, $page, $rep);
  290. if (empty($rep))
  291. {
  292. $this->load->view('site_down', compact('user', 'sitename'));
  293. exit(0);
  294. }
  295. $rep = preg_replace("/,/", "", $rep[1]);
  296. //echo "$rep\n";
  297. // extract number of badges from $page, store in $badge
  298. //preg_match('/iv class="summarycount ar".{10,60} (\d+)<\/d.{10,140}Badges/s', $page, $badge);
  299. if ($trilogy)
  300. $exp = '/ar">(\d+)<\/div><\/td>\s*<td class="summary-header"><h2>Badges/s';
  301. else
  302. $exp = '/iv class="summarycount".{10,60} (\d+)<\/d.{10,140}Badges/s';
  303. preg_match($exp, $page, $badge);
  304. $badge = $badge[1];
  305. //echo "$badge\n";
  306. //exit(0); //return;
  307. // extract questions from $page, store in $questions (array)
  308. // for a $q in $questions:
  309. // $q[1] => votes
  310. // $q[2] => question id
  311. // $q[3] => question text
  312. if ($trilogy)
  313. $exp = '/question-summary narrow.*?>.*?mini-counts">(-?\d+)<.*?\/questions\/(\d*).*?>(.*?)<\/a>/s';
  314. else
  315. $exp = '/question-summary narrow.*?>(-?\d+)<.*?\/questions\/(\d*).*?>(.*?)<\/a>/s';
  316. preg_match_all($exp, $questionsapi, $questions, PREG_SET_ORDER);
  317. //print_r($questions);
  318. //return;
  319. // extract answers from $page, store in $answers (array)
  320. // for an $a in $answers:
  321. // $a[1] => votes
  322. // $a[2] => answer id
  323. // $a[3] => answer text
  324. if ($trilogy)
  325. $exp = '/answer-votes.*?>([-\d]*).*?#(\d*)".*?>([^<]*)/';
  326. else
  327. $exp = '/answer-votes.*?>([-\d]*).*?#(\d*)">([^<]*)/';
  328. preg_match_all($exp, $answersapi, $answers, PREG_SET_ORDER);
  329. //print_r($answers);
  330. //exit(0);
  331. $exp = '/"answers".*?<div.*?>(\d+)/s';
  332. preg_match_all($exp, $page, $ac, PREG_SET_ORDER);
  333. $answercount = $ac[0][1];
  334. //print_r($ac);
  335. //return;
  336. print_r(compact('questions', 'answers', 'answercount', 'rep', 'badge', 'dbitem')); exit(0);
  337. //$answercount = $ac[1];
  338. //echo($ac[1]);
  339. //$answercount = count($answers);
  340. //$answercount=281;
  341. // we've gone this far, but now we have to initialize the db
  342. $this->load->database();
  343. // get existing profile and insert updated one
  344. $dbitem = $this->db->query("SELECT * FROM profile WHERE user = '$user' AND site = '$siteid' ORDER BY date DESC LIMIT 1")->row();
  345. $this->db->query("INSERT INTO profile VALUES('$rep', '$badge','".count($questions)."','".$answercount."','".time()."','$user','$siteid')");
  346. // if we're a new user
  347. if (!$dbitem)
  348. {
  349. $dbitem = (object) array('questions' => 0, 'answers' => 0, 'rep' => 0, 'badges' => 0, 'date' => 0, 'user' => $user);
  350. }
  351. // get chart data
  352. // $low = time()-2592000;
  353. $query = $this->db->query("SELECT rep, questions, answers, date FROM profile WHERE user = '$user' AND site = '$siteid' ORDER BY date ASC");
  354. $data = "";
  355. if ($query->num_rows() > 1)
  356. {
  357. $data = "";
  358. foreach ($query->result() as $row)
  359. {
  360. $r = $row->rep;
  361. $q = $row->questions;
  362. $a = $row->answers;
  363. $pd = $row->date;
  364. $d = $pd."000";
  365. $data .= "[$d, $r, $q, $a],\n";
  366. }
  367. $data = substr_replace($data, "", -2); // remove last comma
  368. }
  369. else
  370. $data = false;
  371. //print_r($profile);
  372. $this->load->view('header', compact('user', 'sitename'));
  373. $this->load->view('overview', compact('questions', 'answers', 'answercount', 'rep', 'badge', 'dbitem'));
  374. //$this->load->view('reputation', array('site' => $site, 'posts' => $this->_readapijson($apijson, $user, $siteid)));
  375. $this->load->view('questans', array('site' => $site, 'siteid' => $siteid, 'stuff' => $questions, 'count' => count($questions), 'name' => "questions <font color=\"AAAAAA\"><small><i>(<a href=\"http://$site/questions/ask\"><font color=\"999999\">ask</font></a>)</i></small></font>"));
  376. ////$this->load->view('questans', array('stuff' => $answers, 'count' => $answercount, 'name' => 'answers <font color="AAAAAA"><small><i>(<a href="http://stackoverflow.com/questions"><font color="999999">answer</font></a>)</i></small></font>'));
  377. $this->load->view('answers', array('site' => $site, 'answers' => $this->_readstats($answersapi, $siteid), 'count' => $answercount));
  378. if ($data)
  379. $this->load->view('rep', compact('data', 'user', 'siteid'));
  380. $after = microtime(true);
  381. $pageload = number_format($during-$before, 2, '.', '');
  382. //$page2load = number_format($during-$between, 2, '.', '');
  383. $dbprocess = number_format($after-$during, 2, '.', '');
  384. $startofday = mktime(0, 0, 0, date('m'), date('d'), date('Y'));
  385. $startofweek = mktime(0, 0, 0, date('m'), date('d')-date('w'), date('Y'));
  386. $startofmonth = mktime(0, 0, 0, date('m'), 1, date('Y'));
  387. $startofalltime = 0;
  388. $today = $this->db->query("SELECT COUNT(*) as c FROM profile WHERE user = '$user' AND site = '$siteid' AND date >= $startofday")->row();
  389. $week = $this->db->query("SELECT COUNT(*) as c FROM profile WHERE user = '$user' AND site = '$siteid' AND date >= $startofweek")->row();
  390. $month = $this->db->query("SELECT COUNT(*) as c FROM profile WHERE user = '$user' AND site = '$siteid' AND date >= $startofmonth")->row();
  391. $all = $this->db->query("SELECT COUNT(*) as c FROM profile WHERE user = '$user' AND site = '$siteid' AND date >= $startofalltime")->row();
  392. $updates = "".$today->c." today";
  393. if ($week->c > $today->c) $updates .= ", ".$week->c." this week";
  394. if ($month->c > $week->c) $updates .= ", ".$month->c." this month";
  395. $updates .= ", ".$all->c." total";
  396. //$updates = "".$today->c." today, ".$week->c." this week, ".$month->c." this month, ".$all->c." total";
  397. $this->load->view('timer', compact('pageload', 'dbprocess', 'dbitem', 'updates'));
  398. $this->load->view('footer');
  399. }
  400. function stats($order='last', $num='30', $site='0', $lt='0')
  401. {
  402. if (file_exists("sof.db-journal"))
  403. unlink("sof.db-journal");
  404. $this->load->database();
  405. $where = "1=1";
  406. $having = "1=1";
  407. if (!strcmp($site, '-1'))
  408. $where = "site > 4 AND $where";
  409. else if (strcmp($site, '0'))
  410. $where = "site = $site AND $where";
  411. if (strcmp($lt, '0'))
  412. $having = "count(user) >= $lt AND $having";
  413. if (!strcmp($order, ""))
  414. $order = "top";
  415. if (!strcmp($order, "last"))
  416. $ob = "max(date) DESC";
  417. if (!strcmp($order, "top"))
  418. $ob = "count(user) DESC";
  419. if (!strcmp($order, "first"))
  420. $ob = "min(date) ASC";
  421. if (!strcmp($order, "newbies"))
  422. $ob = "min(date) DESC";
  423. if (!strcmp($order, "user"))
  424. $ob = "user ASC";
  425. $query = $this->db->query("SELECT * FROM sites");
  426. //print_r($query->result_array());
  427. $sites = array();
  428. foreach ($query->result_array() as $row)
  429. $sites[$row['id']] = $row;
  430. //print_r($sites);
  431. $query = $this->db->query("SELECT user, site, count(user), max(date), min(date) FROM profile WHERE $where GROUP BY user, site HAVING $having ORDER BY $ob LIMIT $num");
  432. //$result = mysql_query($query);
  433. $this->load->view('stats', compact('sites', 'query', 'order', 'num', 'site', 'lt'));
  434. //echo "asdf";
  435. $this->load->view('footer');
  436. }
  437. function reset()
  438. {
  439. unlink("sof.db-journal");
  440. }
  441. }
  442. ?>