load_from_webpage(); // read in the data * // $pd->save_to_cache_files(); // (or add these two.) * // $pd->read_from_cache_files(); * $pd->html(); // see this for options on output */ namespace Misc { class PolicyDecisions { const WIKIURL = 'wiki.cacert.org/'; const HTTP = 'http:'; const HTTPS ='https:'; const WIKI = 'https://wiki.cacert.org'; const POLICY_DECISIONS = 'http://wiki.cacert.org/PolicyDecisions'; const POLICY_DECISIONSS = 'https://wiki.cacert.org/PolicyDecisions#'; private $votes; private $urls; private $names; private $decisions; private $state; private $line_num; private function &read_all(&$handle) { $contents = array(); while (!feof($handle)) { $contents[] = fread($handle, 8192); } fclose($handle); $x = join('', $contents); // echo "opened $url, found " . strlen($x) . " chars\n
"; return $x; } private function &read_page($url) { $handle = fopen($url, 'rb'); $x = $this->read_all($handle); return $x; } private function &read_file($name) { $x = ''; if (!file_exists($name)) return $x; $handle = fopen($name, 'rb'); $x = $this->read_all($handle); return $x; } public function save_to_cache_files() { $test = true; $this->write_cache_file('decisions', $this->decisions, 'decision', $test); $this->write_cache_file('votes', $this->votes, '', $test); $this->write_cache_file('names', $this->names, 'index', $test); } public function read_from_cache_files() { $this->decisions = $this->read_cache_file('decisions'); $this->votes = $this->read_cache_file('votes'); $this->names = $this->read_cache_file('names'); } public function write_cache_file($name, &$list, $keyname, $test = false) { $handle = fopen( $this->name_to_filename($name), 'wb' ); $time = time(); $count = count($list); $lines = array("v1;$name;$time;$count;json-array;$keyname"); foreach ($list as $entry) { $lines[] = json_encode($entry); } $data = join("\n", $lines); fwrite($handle, $data); fclose($handle); if ($test) { $result = $this->read_cache_file($name); foreach ($list as $key => $entry) { $res_entry = $result[$key]; if (! $res_entry ) { $this->error("failed to recovered $key from cache."); // echo "
\n"; // print_r($entry); // echo "
\n"; // print_r($result); exit(1); } if ($entry != $res_entry) { $this->error("recovered $name not the same."); // echo "
\n"; // print_r(array_diff($res_entry, $entry)); // echo "
\n"; // print_r(array_diff($entry, $res_entry)); // echo "
\n"; // print_r($entry); // echo "
\n"; // print_r($res_entry); // echo "
\n"; exit(); } } if ($result != $list) { $this->error("recovered $name not the same."); // echo "
\n"; // print_r(array_diff($list, $result)); // echo "
\n"; // print_r(array_diff($result, $list)); // echo "
\n"; // print_r($result); // echo "
\n"; // print_r($list); // echo "
\n"; exit(); } } } /* * Prepended to cache files, don't forget a '/' if needed. */ public function &setCachePrefix($prefix) { $this->cache_prefix = $prefix; } private function &name_to_filename($name) { return $this->cache_prefix . $name . '.txt'; } /* * The array key can be set to some string * from the array entry named keyname, if supplied. */ public function &read_cache_file($name) { $data = $this->read_file( $this->name_to_filename($name) ); $lines = explode("\n", $data); $head = explode(';', $lines[0]); $count = $head[3]; $keyname = $head[5]; $count2 = count($lines); if ($count + 1 != count($lines)) { $this->error("$name: $count+1 != $count2, lines mismatch"); } // echo "decision key: $keyname / count == $count
\n"; $output = array(); for ($i = 1; $i <= $count; $i ++) { $recovered = json_decode($lines[$i], true); // echo "$count: from " . $lines[i] . "\n
"; // print_r($recovered); if ($keyname) { $key = $recovered[$keyname]; // echo "\n
recovered $key from $keyname\n
"; $output[$key] = $recovered; } else { $output[] = $recovered; } // exit(); } return $output; } private function error($e) { echo $this->line_num . ": " . $e . "
\n"; } private function herror($e) { echo $this->line_num . ": " . htmlentities($e) . "
\n"; } private function vote_plus_one($name, $url, $act) { $best_uniq = $this->name_of_contributor($name, $url); $current_decision = $this->decision; // note this does not pick up duplicates... // it could, but they shouldn't be in the source anyway. $this->votes[] = array($current_decision, $best_uniq, $act); } private function count_a_vote($vote, $act) { $matches = array(); $v1 = trim($vote); if (!$v1) return; if (preg_match('/]*href="([^"]*)">([^<]*)<\/a>/i', $v1, $matches)) { $name = $matches[2]; $wikiurl = $matches[1]; $this->vote_plus_one($name, $wikiurl, $act); } elseif (preg_match('/([<>;,])/', $v1, $matches)) { $chars = $matches[0]; $this->herror("garbled name with $chars in $vote"); } elseif (preg_match('/^([^<>]*)$/i', $v1, $matches)) { $name = $matches[1]; $this->vote_plus_one($name, '', $act); } else { $this->herror("unparsed name in $vote"); } } private function count_td($line, $act) { $v0 = preg_replace('/]*>/i', '', $line); $v1 = preg_replace('/]*>/i', '', $v0); $v2 = preg_replace('/<\/td>/i', '', $v1); $v3 = preg_replace('/<\/p>/i', '', $v2); // $this->error("stripped:\n$v3 ~~~~~~~~~~~~\n"); $votes = explode(",", $v3); foreach ($votes as $v) { // echo " [$v] "; $this->count_a_vote($v, $act); } } private function get_name($url) { return $this->names[$url]['name']; } private function get_best_uniq_name($name, $url) { return $url ? $url : $name; } private function get_wikiname($name) { foreach ($this->names as $n) { if ($n['name'] == $name) return $n['url']; // if there is one, else empty string } return ''; } private function name_of_contributor($name, $url = '') { if ($url) { if (! array_key_exists($url, $this->names)) { $this->names[$url] = array( 'url' => $url, 'name' => $name, 'index' => $url, ); } return $url; } else { if (! array_key_exists($name, $this->names)) $this->names[$name] = $name; $this->names[$name] = array( 'url' => '', 'name' => $name, 'index' => $name, ); return $name; } } private function resolved_by($name, $url = '') { $decision = $this->decision; $this->decisions[$decision]['name'] = $name; // not needed $this->decisions[$decision]['url'] = $url; $this->vote_plus_one($name, $url, 'res'); } private function state($l) { /* * Extract the decision number and title from the Heading

*/ if (preg_match('/

/i', $l, $matches)) { $decision = trim($matches[1]); $title = trim($matches[2]); $title = preg_replace('/_/', ' ', $title); $this->decision = $decision; $this->decisions[$decision] = array( 'title' => trim($title), 'decision' => trim($decision), ); $this->resolved = 1; } /* * If we've just seen a decision number, then look out for who * called for the resolution. */ if (1 == $this->resolved) { if (preg_match('/]*href="([^"]*)">([^<]*)<\/a>: *Resolved/i', $l, $matches)) { $url = trim($matches[1]); $name = trim($matches[2]); $this->resolved_by($name, $url); $this->name_of_contributor($name, $url); $this->resolved = 0; // echo "{$this->decision} matching '$url' / $name /
\n"; } elseif (preg_match('/>([^:<>]*): *Resolved/i', $l, $matches)) { $name = trim($matches[1]); $this->resolved_by($name); // echo "{$this->decision} matching '$name'
\n"; $this->resolved = 0; } // else, the name of resolution proposer is not properly recorded. } /* * Search for the Votes, set state to count them on next line. */ if ('' == $this->vote_state) { if (preg_match("/aye:/i", $l)) $this->vote_state = 'aye'; elseif (preg_match("/nay:/i", $l)) $this->vote_state = 'nay'; elseif (preg_match("/abstain:/i", $l)) $this->vote_state = 'abs'; } elseif ($this->vote_state) { /* * Counting votes. */ // $this->error("looking for TD\n===================\n$l\n-------------------"); if (preg_match("//i", $l)) { $this->count_td($l, $this->vote_state); } $this->vote_state = ''; } } public function load_from_webpage() { $data = $this->read_page(self::POLICY_DECISIONS); $lines = explode("\n", $data); $this->vote_state = ''; $this->resolved = 0; $this->names = array(); $this->votes = array(); $this->decisions = array(); $this->line_num = 0; foreach ($lines as $l) { $this->line_num++; $this->state($l); } } public function html_head($title) { $this->l(""); $this->l(""); $this->l(""); $this->l(" "); $this->l(" $title"); $this->l("\n"); } function setLineWriter($x) { $this->lw = $x; } private function l($s, $and_this_comment = '') { if ($this->lw) { $this->lw->l($s, $and_this_comment); } else { $this->out[] = $s; } } public function init_output(&$lines = null) { if ($lines) { $this->out &= $lines; } else { $this->out = array(); } } /* * This is the major entry point for a simple output. * Copy this and vary the output initialisation, and choice of tables. */ public function html() { $this->init_output(); $this->html_head('Policy Group Decisions Summary'); $this->html_by_contributors(); $this->html_by_decisions(); $this->html_tail('Policy Group Decisions Summary'); $this->l(""); echo join("\n", $this->out); } public function html_by_contributors() { $count_contributors = count($this->names); $votes_by_contributor = $this->get_votes_by_contributor(); $count_acts = count($this->votes); $count_decisions = count($this->decisions); $this->l("

The Policy Group Hall of Fame

"); $this->l("

Policy Group has $count_contributors contributors, with a total $count_acts acts of participation. Each act is a vote cast or a decision proposed.

"); $this->l("

"); $this->table_votes($votes_by_contributor); } public function html_tail() { $this->l("
"); $url = self::POLICY_DECISIONS; $link = "
$url"; $this->l(" This summary wiki-scraped from $link, the formal record of Policy Decisions of the Policy Group. "); $this->l(""); } public function html_by_decisions() { $this->l("

"); $this->l("

Summary of Decisions

\n

$count_decisions decisions issued by Policy Group

"); $this->l("

"); $this->table_decisions(); } private function count_votes(&$votes_by_contributor) { $count = 0; foreach ($votes_by_contributor as $entry) { $count += $entry[3]; } return $count; } static function cmp($a, $b) { if ($a['count'] < $b['count']) return +1; if ($a['count'] > $b['count']) return -1; return 0; } private function &get_votes_by_contributor() { $sort = array(); foreach ($this->votes as $vote) { $decision = $vote[0]; $uniq = $vote[1]; $act = $vote[2]; if (! array_key_exists($uniq, $sort)) { $sort[$uniq] = array( 'uniq' => $uniq, 'decisions' => array(), 'count' => 0, 'aye' => 0, 'nay' => 0, 'res' => 0, 'abs' => 0, ); } $sort[$uniq]['decisions'][] = $decision; switch ($act) { case 'aye': case 'nay': case 'res': case 'abs': $sort[$uniq][$act] += 1; break; default: $this->error("$decision: $uniq = $act? is what act?"); } $sort[$uniq]['count'] += 1; } // echo "OK, class name is " . get_class($this) . "
\n"; usort($sort, array(get_class($this), "cmp")); // class name return $sort; } private function table_votes(&$votes_by_contributor) { $this->l(""); $this->l(""); foreach ($votes_by_contributor as $entry) { $most_actions = $entry['count']; break; } $this->l(" "); $this->l(" "); $this->l(""); $td = ""; $widths = str_repeat($td, $most_actions); $this->l(" $widths "); foreach ($votes_by_contributor as $url => $entry) { $this->l($this->get_contributor_line($entry, $most_actions)); } $this->l("
Name#ActsAyeNayAbsRes


\n"); } private function get_contributor_line($entry, $most_actions) { $name = $this->get_name_link($entry['uniq']); $count = $entry['count']; $decisions = $entry['decisions']; $aye = $entry['aye'] ? $entry['aye'] : ' '; $nay = $entry['nay'] ? $entry['nay'] : ' '; $abs = $entry['abs'] ? $entry['abs'] : ' '; $res = $entry['res'] ? $entry['res'] : ' '; $lead = " " . "$name" . "{$entry['count']}" . "\n"; $mix = array(); $purl = self::POLICY_DECISIONSS; for ($i = 1; $i <= $most_actions; $i++) { $d = array_pop($decisions); if ($d) { $mix[] = "" . "*" . ""; } else { $mix[] = ""; } } $final = "$aye" . "$nay" . "$abs" . "$res" . "" . "\n"; $x = $lead . join('', $mix) . $final; return $x; } private function table_decisions() { $this->l(""); $this->l(""); foreach ($this->decisions as $number => $decision) { $title = $decision['title']; $name = $decision['name']; $url = $decision['url']; $name = $this->to_name_link($name, $url); $pnumber = '$number"; $this->l(" "); } $this->l("
NumberProposorTitle
$pnumber$name$title
"); } private function to_name_link($name, $url) { if ($url && ($url != $name)) { if (preg_match(",^/,", $url)) $url = self::WIKI . $url; $name = "$name"; } return $name; } private function get_name_link($uniq) { if ('/' == $uniq[0]) { // is a url-based name $url = $uniq; $name = $this->get_name($uniq); $display = "$name"; } else { $display = $uniq; } return $display; } } } /* local test code... $pd = new PolicyDecisions(); $pd->load_from_webpage(); // $pd->save_to_cache_files(); // $pd->read_from_cache_files(); $pd->html(); */ ?>