You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@labs.apache.org by co...@apache.org on 2007/01/20 22:21:18 UTC
svn commit: r498170 [1/2] - in /labs/pulse/report-web: ./
liststats-classes.php mlists.php util-classes.php
Author: coar
Date: Sat Jan 20 13:21:18 2007
New Revision: 498170
URL: http://svn.apache.org/viewvc?view=rev&rev=498170
Log:
Scripts for generating the Web status page
Added:
labs/pulse/report-web/
labs/pulse/report-web/liststats-classes.php (with props)
labs/pulse/report-web/mlists.php (with props)
labs/pulse/report-web/util-classes.php (with props)
Added: labs/pulse/report-web/liststats-classes.php
URL: http://svn.apache.org/viewvc/labs/pulse/report-web/liststats-classes.php?view=auto&rev=498170
==============================================================================
--- labs/pulse/report-web/liststats-classes.php (added)
+++ labs/pulse/report-web/liststats-classes.php Sat Jan 20 13:21:18 2007
@@ -0,0 +1,1055 @@
+<?php
+/*
+ * ListStats database access classes.
+ */
+Include_Once('util-classes.php');
+
+define('LSDB_SUCCESS', 200);
+define('LSDB_NOSUCH', -404);
+define('LSDB_AMBIGUOUS', -300);
+define('LSDB_UNCHANGED', -304);
+define('LSDB_INTERNAL_ERROR', -500);
+define('LSDB_CURRENT', -1024);
+define('LSDB_THIS_URI', -1025);
+
+/*
+ * Base class.
+ */
+class lsdb__baseclass {
+ var $_record;
+ var $_custom;
+ var $_new = false;
+ var $_dirty = false;
+ var $_changed;
+ var $_error;
+ var $_message;
+
+ /*
+ * Self-explanatory, I hope.
+ */
+ function isNew($newval=null) {
+ $oldval = ($this->_new ? true : false);
+ if (isset($newval)) {
+ $this->_new = ($newval ? true : false);
+ }
+ return $oldval;
+ }
+
+ function isDirty($newval=null) {
+ $oldval = ($this->_dirty ? true : false);
+ if (isset($newval)) {
+ $this->_dirty = ($newval ? true : false);
+ }
+ return $oldval;
+ }
+
+ /*
+ * Normalise a key name from possible mixed case to whatever we use.
+ */
+ function nkey($keyname) {
+ return strtolower($keyname);
+ }
+
+ /*
+ * Return a value from the object.
+ */
+ function get($fname) {
+ $fname = $this->nkey($fname);
+ return (isset($this->_record[$fname]) ? $this->_record[$fname] : null);
+ }
+
+ /*
+ * Return a non-db-backed value from the object.
+ */
+ function cget($fname) {
+ $fname = $this->nkey($fname);
+ return (isset($this->_custom[$fname]) ? $this->_custom[$fname] : null);
+ }
+
+ /*
+ * Set a cell value for the object.
+ */
+ function set($fname, $val=null) {
+ if (is_array($fname)) {
+ $result = array();
+ foreach ($fname as $k => $v) {
+ $oldval = $this->set($k, $v);
+ $result[$k] = $oldval;
+ }
+ return $result;
+ }
+ $fname = $this->nkey($fname);
+ $oldval = $this->get($fname);
+ $this->_record[$fname] = $val;
+ if ($val != $oldval) {
+ if (! is_array($this->_changed)) {
+ $this->_changed = array();
+ }
+ if (! array_key_exists($fname, $this->_changed)) {
+ $this->_changed[$fname] = $oldval;
+ }
+ }
+ $this->isDirty($this->isDirty() || ($val != $oldval));
+ return $oldval;
+ }
+
+ /*
+ * Clear a cell.
+ */
+ function reset($key=null) {
+ if (! isset($key)) {
+ $this->_record = null;
+ }
+ else {
+ unset($this->_record[$this->nkey($key)]);
+ }
+ }
+ /*
+ * Alias name that might be clearer
+ */
+ function clear($key=null) {
+ return $this->reset($key);
+ }
+
+ /*
+ * Set a non-db-backed cell value for the object.
+ */
+ function cset($fname, $val=null) {
+ if (is_array($fname)) {
+ $result = array();
+ foreach ($fname as $k => $v) {
+ $oldval = $this->cset($k, $v);
+ $result[$k] = $oldval;
+ }
+ return $result;
+ }
+ $fname = $this->nkey($fname);
+ $oldval = $this->cget($fname);
+ $this->_custom[$fname] = $val;
+ return $oldval;
+ }
+
+ /*
+ * Return a list of the fields in the record (debugging purposes).
+ */
+ function fields() {
+ return $this->_record;
+ }
+
+ /*
+ * Return the structure of the current object as a printable
+ * string (á la print_r()) but with the option of avoiding
+ * recursion.
+ */
+ function as_string($include_objects=false) {
+ $output = print_r($this, true);
+ if (! $include_objects) {
+ $lines = preg_split('/[\r\n]/', $output);
+ $composite = array();
+ $skipping = false;
+ $start_next = false;
+ foreach ($lines as $line) {
+ if (! $skipping) {
+ if ($start_next) {
+ $stop_on = preg_replace('/\(/', ')', $line);
+ $start_next = false;
+ $skipping = true;
+ continue;
+ }
+ $composite[] = $line;
+ if (preg_match('/^(\s*\[).*\s+Object\s*$/', $line)) {
+ $start_next = true;
+ }
+ }
+ else {
+ if ($line != $stop_on) {
+ continue;
+ }
+ $skipping = false;
+ }
+ }
+ $output = implode("\n", $composite);
+ }
+ return $output;
+ }
+
+ /*
+ * Set an arbitrary object value.
+ */
+ function _set(&$cell, $newval=null) {
+ $rval = $cell;
+ if (isset($newval)) {
+ $cell = $newval;
+ }
+ return $rval;
+ }
+
+ /*
+ * Per-object error codes.
+ */
+ function clear_error() {
+ $this->error(0);
+ $this->message('');
+ }
+
+ function error($newval=null) {
+ return $this->_set($this->_error, $newval);
+ }
+
+ function message($newval=null) {
+ return $this->_set($this->_message, $newval);
+ }
+
+}
+
+/*
+ * Try a class for the database stuff, to partition the db handle.
+ */
+class lsDB extends lsdb__baseclass {
+ var $_dbo = 0;
+ var $_dbc = 0;
+ var $_dbh = 0;
+ var $_errno = 0;
+ var $_errstr = '';
+ var $_db_tablename;
+ var $_id_fieldname;
+ var $_id_display;
+ var $_object_type;
+ var $_sql;
+ var $_db;
+ var $_query;
+ var $_key;
+ var $_checksum;
+
+ /*
+ * Class constructor.
+ */
+ function lsDB($host='localhost', $username=null, $pw=null) {
+ $this->open($host, $username, $pw);
+ $this->_dbo =& $this;
+ $sql = 'SHOW TABLES';
+ $q = $this->query($sql);
+ while ($q && ($row = mysql_fetch_row($q))) {
+ list($table) = $row;
+ $this->_db[] = $table;
+ }
+ foreach ($this->_db as $table) {
+ $sql = "DESCRIBE $table";
+ $q = $this->query($sql);
+ while ($q && ($row = mysql_fetch_row($q))) {
+ $this->_db[$table][$row[0]] = $row[1];
+ }
+ }
+ $this->importdb($this->_dbo);
+ }
+
+ /*
+ * Method (doesn't really need to be a method, but o well) to
+ * add a condition segment to a MySQL WHERE clause.
+ */
+ function where($field, $value, $comp='=', $verbatim=false) {
+ $clause = '';
+ if (isset($value)) {
+ if ($verbatim) {
+ $clause = "($field $comp $value) ";
+ }
+ else {
+ $clause = "($field $comp '" . AddSlashes($value) . "') ";
+ }
+ }
+ return $clause;
+ }
+
+ function id($field, $table, $addl=null) {
+ $this->_id_fieldname = $field;
+ $this->_db_tablename = $table;
+ $this->_id_display = $addl;
+ }
+
+ function is_valid_field($table, $field) {
+ if (array_key_exists($table, $this->_dbo->_db)
+ && array_key_exists($field, $this->_dbo->_db[$table])) {
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * The only errors we care about, really, are MySQL ones.
+ * The query() method updates the error cells directly, but
+ * calls such as mysql_fetch_row() aren't methods. So we
+ * need to copy them from MySQL into our object before
+ * returning them.
+ */
+ function errno() {
+ $this->_errno = mysql_errno($this->_dbc);
+ return $this->_errno;
+ }
+
+ function errstr() {
+ $this->_errstr = mysql_error($this->_dbc);
+ return $this->_errstr;
+ }
+
+ function sql() {
+ return $this->_sql;
+ }
+
+ /*
+ * Load the latest values from the database.
+ */
+ function refresh($key=null) {
+ if (! isset($key)) {
+ $key = $this->_key;
+ }
+ $sql = 'SELECT * FROM ' . $this->_db_tablename
+ . ' WHERE ' . $this->where($this->_id_fieldname, $key);
+ $q = $this->query($sql);
+ if ($q && (mysql_num_rows($q) == 1)) {
+ $this->isNew(false);
+ $this->isDirty(false);
+ $this->_record = mysql_fetch_assoc($q);
+ $this->_key = $this->get($this->_id_fieldname);
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * Delete the current record from the database.
+ * A return of 0 means the item was deleted from the database and
+ * the object pointer set to null; -1 means we don't know what to
+ * do (bad object constructor or maybe not a deletable thing);
+ * -2 means there's no single record to delete (either doesn't
+ * exist or ambiguous). Anything else is a MySQL error number.
+ */
+ function delete() {
+ $this->clear_error();
+ if ((! isset($this->_db_tablename))
+ || (! isset($this->_id_fieldname))
+ || (! $this->get($this->_id_fieldname))) {
+ $this->message('Internal object error; ID fields not set');
+ return $this->error(LSDB_INTERNAL_ERROR);
+ }
+ $tname = $this->_db_tablename;
+ $fname = $this->_id_fieldname;
+ $fval = $this->get($fname);
+ if (! $fval) {
+ $this->message('Object not identified');
+ return $this->error(LSDB_INTERNAL_ERROR);
+ }
+
+ $sql = "SELECT COUNT(*) FROM $tname WHERE "
+ . $this->where($fname, $fval);
+ $q = $this->query($sql);
+ if ($this->errno()) {
+ $this->message($this->errstr());
+ return $this->error($this->errno());
+ }
+ list($n) = mysql_fetch_row($q);
+ if ($n < 1) {
+ $this->message('No such record in the database');
+ return $this->error(LSDB_NOSUCH);
+ }
+ else if ($n > 1) {
+ $this->message("'$ident' is an ambiguous record identifier");
+ return $this->error(LSDB_AMBIGUOUS);
+ }
+
+ $sql = "DELETE FROM $tname WHERE "
+ . $this->where($fname, $fval);
+ $subject = 'Deleted '
+ . strtoupper($this->_db_tablename)
+ . ' record';
+ $more_id = $this->commit_subject();
+ $subject .= ($more_id ? " $more_id" : '');
+ $subject .= ' ('
+ . $this->_id_fieldname
+ . ' '
+ . $this->get($this->_id_fieldname)
+ . ')';
+ $message = "\n";
+ $q = $this->query($sql);
+ if (! $this->errno()) {
+ $message .= "Operation succesful.\n";
+ mail('Rodent of Unusual Size <Ke...@Golux.Com>',
+ "AcDB: $subject",
+ $message);
+ return 0;
+ }
+ $message .= "Operation failed:\n\n"
+ . " SQL='" . $this->_sql . "'\n"
+ . " errno=" . $this->errno() . "\n"
+ . " error='" . $this->errstr() . "'\n";
+ mail('Rodent of Unusual Size <Ke...@Golux.Com>',
+ "AcDB: DB FAILURE: $subject",
+ $message);
+ $this->message($this->errstr());
+ return $this->error($this->errno());
+ }
+
+ /*
+ * Copy the database bits to the current object (not used in the
+ * acDB class itself, but inherited).
+ */
+ function importdb($dbo) {
+ while (is_object($dbo) && (! is_a($dbo, 'lsDB'))) {
+ $dbo =& $dbo->_dbo;
+ }
+ $this->_dbo =& $dbo;
+ $this->_dbc = $dbo->_dbc;
+ $this->_dbh = $dbo->_dbh;
+ $this->_errno = $dbo->_errno;
+ $this->_errstr = $dbo->_errstr;
+ }
+
+ /*
+ * Close the connexion. Hardly necessary, but just for completeness..
+ */
+ function close() {
+ if ($this->_dbc != 0) {
+ mysql_close($this->_dbc);
+ $this->_dbc = 0;
+ }
+ $this->_dbh = 0;
+ return true;
+ }
+
+ /*
+ * Access the database server and open the database.
+ */
+ function open($host='localhost', $username=null, $pw=null) {
+ if ($this->_dbc != 0) {
+ return mysql_ping($this->_dbc);
+ }
+ if (! isset($username)) {
+ $this->_dbc = mysql_pconnect($host);
+ }
+ else if (! isset($pw)) {
+ $this->_dbc = mysql_pconnect($host, $username);
+ }
+ else {
+ $this->_dbc = mysql_pconnect($host, $username, $pw);
+ }
+ if ($this->_dbc) {
+ $this->_dbh = mysql_select_db('<dbname>');
+ }
+ else {
+ $this->_errno = -666;
+ $this->_errstr = "unable to connect to database host '$host'";
+ return;
+ }
+ $this->setknownerrstate();
+ }
+
+ /*
+ * Function to issue a query inside the object.
+ */
+ function query($sql) {
+ $this->_sql = $sql;
+ $this->_query = mysql_query($sql, $this->_dbc);
+ $this->setknownerrstate();
+ return $this->_query;
+ }
+
+ function release($q=null) {
+ $query = $q;
+ $status = true;
+ if (! isset($q)) {
+ $query = $this->_query;
+ }
+ if (is_resource($query)) {
+ $status = mysql_free_result($query);
+ if ($status && (! isset($q))) {
+ $this->_query = null;
+ }
+ }
+ return $status;
+ }
+
+ /*
+ * Set our error cells to a known state: either no error, stuckage,
+ * or something from the last db action.
+ */
+ function setknownerrstate() {
+ if ((! isset($this->_dbc)) || (! $this->_dbc)) {
+ $this->_errno = -666;
+ $this->_errstr = 'no database connexion active';
+ return;
+ }
+ if ($merr = mysql_errno($this->_dbc)) {
+ $this->_errno = $merr;
+ $this->_errstr = mysql_error($this->_dbc);
+ }
+ else {
+ $this->_errno = 0;
+ $this->_errstr = '';
+ }
+ return $this->_errno;
+ }
+
+ /*
+ * Commit function..
+ */
+ function commit($altkey=null) {
+ if (! $this->isDirty()) {
+ return null;
+ }
+ if ($this->isNew()) {
+ $sql = 'INSERT INTO ';
+ $subject = 'New ';
+ $updating = false;
+ }
+ else {
+ $sql = 'UPDATE ';
+ $subject = 'Updated ';
+ $updating = true;
+ }
+ $subject .= strtoupper($this->_db_tablename)
+ . ' record ';
+ $more_id = $this->commit_subject();
+ if ($more_id) {
+ $subject .= $more_id;
+ }
+ else {
+ $subject .= $this->get($this->_id_fieldname);
+ if (isset($this->_id_display)) {
+ $subject .= '/'
+ . $this->get($this->_id_display);
+ }
+ }
+ $message = "\n";
+ $sql .= $this->_db_tablename . ' SET ';
+ $joiner = '';
+ $dbo = $this->_dbo;
+ foreach ($this->_record as $k => $v) {
+ if (! isset($dbo->_db[$this->_db_tablename][$k])) {
+ continue;
+ }
+ $sql .= $joiner
+ . " $k = '"
+ . AddSlashes($v)
+ . "'";
+ $joiner = ',';
+ if ($updating
+ && is_array($this->_changed)
+ && array_key_exists($k, $this->_changed)) {
+ $last = $this->_changed[$k];
+ if (! preg_match('/[\r\n]\s*$/', $last)) {
+ $last .= "\n";
+ }
+ $message .= " [$k]:\n old: $last new: $v";
+ }
+ else {
+ $message .= " [$k]:\n $v";
+ }
+ if (! preg_match('/[\r\n]\s*$/', $v)) {
+ $message .= "\n";
+ }
+ }
+ if (! $this->isNew()) {
+ $sql .= ' WHERE '
+ . $this->where($this->_id_fieldname,
+ $this->get($this->_id_fieldname));
+ }
+ $abort = $this->precommit();
+ if ($abort) {
+ $message = "\nCommit aborted by precommit function\n\n $abort\n";
+ $subject = "lsDB: ABORTED: $subject";
+ mail('Rodent of Unusual Size <Ke...@Golux.Com>',
+ $subject,
+ $message);
+ return LSDB_INTERNAL_ERROR;
+ }
+ $q = $this->query($sql);
+ if ($this->errno()) {
+ $message = "\nOperation failed:\n"
+ . ' error=[' . $this->errno() . "] '"
+ . $this->errstr() . "'\n"
+ . " SQL='" . $this->_sql . "'\n\n"
+ . $message;
+ $subject = "lsDB: DB FAILURE: $subject";
+ mail('Rodent of Unusual Size <Ke...@Golux.Com>',
+ $subject,
+ $message);
+ return false;
+ }
+ $this->isDirty(false);
+ if ($this->isNew()) {
+ $this->isNew(false);
+ if ((! isset($altkey)) || $altkey) {
+ $id = mysql_insert_id($this->_dbc);
+ if (! isset($altkey)) {
+ $altkey = $this->_id_fieldname;
+ }
+ $this->_record[$altkey] = $id;
+ }
+ $subject .= " ($altkey $id)";
+ }
+ $subject = "lsDB: $subject";
+ mail('Rodent of Unusual Size <Ke...@Golux.Com>',
+ $subject,
+ $message);
+ return $this->_record[$this->_id_fieldname];
+ }
+
+ /*
+ * Method for generating info for the commit message subject line.
+ * Other classes get to supercede it.
+ */
+ function commit_subject() {
+ return null;
+ }
+
+ /*
+ * Method for doing any just-in-time things or checks before committing.
+ * Other classes get to supercede it. If it returns null, the
+ * commit proceeds, otherwise it's aborted and the return value is
+ * treated as the reason.
+ */
+ function precommit() {
+ return null;
+ }
+
+ function latest() {
+ $sql = 'SELECT xid FROM loadinfo ORDER BY asof DESC LIMIT 1 ';
+ $q = $this->query($sql);
+ if ($q) {
+ list($xid) = mysql_fetch_row($q);
+ $o_asof = new lsLoadInfo($this->_dbo, $xid);
+ return $o_asof->get('asof');
+ }
+ return null;
+ }
+
+ function summary($list, $from, $until=null) {
+ return new lsMListSummary($this->_dbo, null, $list, $from, $until);
+ }
+ function mlist($id=null) {
+ return new lsMList($this->_dbo, $id);
+ }
+ function infotext($id=null) {
+ return new lsInfoText($this->_dbo, $id);
+ }
+
+
+ function select($criteria=null, $order=null) {
+ $o_selection = new lsSelection($this->_dbo, $criteria, $order);
+ return $o_selection;
+ }
+ function mlists($criteria=null, $order=null, $xids_only=false) {
+ $o_selection = $this->select($criteria, $order);
+ return ($xids_only ? $o_selection->xids() : $o_selection->mlists());
+ }
+
+ function domain($id=null) {
+ return new lsDomain($this->_dbo, $id);
+ }
+
+ function domains($criteria=null, $order=null) {
+ $sql = 'SELECT DISTINCT domain FROM list ';
+ if (isset($criteria) && is_array($criteria)) {
+ $first = true;
+ foreach ($criteria as $criterion => $value) {
+ if ($this->is_valid_field('list', $criterion)) {
+ $badwhere = false;
+ $sql .= ($first ? ' WHERE ' : ' AND ');
+ $first = false;
+ if (is_array($value)) {
+ $sql .= $this->where($criterion, $value[0], $value[1]);
+ }
+ else {
+ $sql .= $this->where($criterion, $value);
+ }
+ }
+ }
+ }
+ if (isset($order)) {
+ if (is_string($order)) {
+ $sql .= " ORDER BY $order";
+ }
+ else if (is_array($order)) {
+ $sql .= ' ORDER BY';
+ $first = true;
+ foreach ($order as $k => $v) {
+ $sql .= ($first ? '' : ', ')
+ . " $k $v";
+ $first = false;
+ }
+ }
+ }
+ else {
+ $sql .= ' ORDER BY domain ASC ';
+ }
+ $a_result = array();
+ $q = $this->query($sql);
+ while ($q && (list($domain) = mysql_fetch_row($q))) {
+ $a_result[] = $this->domain($domain);
+ }
+ return $a_result;
+ }
+
+}
+
+class lsLoadInfo extends lsDB {
+
+ function lsLoadInfo($dbo, $id=null) {
+ $this->id('xid', 'loadinfo');
+ $this->importdb($dbo);
+ if (! isset($id)) {
+ $this->isNew(true);
+ return;
+ }
+ if (is_numeric($id)) {
+ $this->refresh((int)$id);
+ return;
+ }
+ $sql = 'SELECT xid FROM loadinfo WHERE '
+ . $this->where('asof', $id);
+ $q = $this->query($sql);
+ if ($q && (mysql_num_rows($q) > 0)) {
+ list($id) = mysql_fetch_row($q);
+ $this->refresh((int)$id);
+ return;
+ }
+ }
+
+ function mlists($criteria=null, $order=null, $xids_only=false) {
+ $sql = 'SELECT xid FROM list WHERE '
+ . $this->where('asof', $this->get('asof'));
+ if (isset($criteria) && is_array($criteria)) {
+ foreach ($criteria as $criterion => $value) {
+ if ($this->is_valid_field('list', $criterion)) {
+ $badwhere = false;
+ $sql .= ' AND ';
+ if (is_array($value)) {
+ $sql .= $this->where($criterion, $value[0], $value[1]);
+ }
+ else {
+ $sql .= $this->where($criterion, $value);
+ }
+ }
+ }
+ }
+ if (isset($order)) {
+ if (is_string($order)) {
+ $sql .= " ORDER BY $order";
+ }
+ else if (is_array($order)) {
+ $sql .= ' ORDER BY';
+ $first = true;
+ foreach ($order as $k => $v) {
+ $sql .= ($first ? '' : ', ')
+ . " $k $v";
+ $first = false;
+ }
+ }
+ }
+ else {
+ $sql .= ' ORDER BY domain ASC, list ASC ';
+ }
+ $a_result = array();
+ $q = $this->query($sql);
+ while ($q && (list($xid) = mysql_fetch_row($q))) {
+ $a_result[] = $xids_only ? $xid : $this->_dbo->mlist($xid);
+ }
+ return $a_result;
+ }
+
+}
+
+class lsSelection extends lsDB {
+ var $_criteria = '';
+ var $_order = '';
+ var $_xids;
+ var $_mlists;
+
+ function lsSelection($dbo, $criteria=null, $order=null) {
+ $this->importdb($dbo);
+ $sql = 'SELECT xid FROM list ';
+ if (isset($criteria)) {
+ if (is_string($criteria)) {
+ $this->_criteria = 'WHERE ' . $criteria;
+ }
+ else if (is_array($criteria)) {
+ $first = true;
+ foreach ($criteria as $criterion => $value) {
+ if ($this->is_valid_field('list', $criterion)) {
+ $badwhere = false;
+ $this->_criteria .= ($first ? ' WHERE ' : ' AND ');
+ $first = false;
+ if (is_array($value)) {
+ $this->_criteria .= $this->where($criterion,
+ $value[0],
+ $value[1]);
+ }
+ else {
+ $this->_criteria .= $this->where($criterion,
+ $value);
+ }
+ }
+ }
+ }
+ }
+ if (isset($order)) {
+ if (is_string($order)) {
+ $this->_order .= " ORDER BY $order";
+ }
+ else if (is_array($order)) {
+ $this->_order .= ' ORDER BY';
+ $first = true;
+ foreach ($order as $k => $v) {
+ $this->_order .= ($first ? '' : ', ')
+ . " $k $v";
+ $first = false;
+ }
+ }
+ }
+ else {
+ $this->_order .= ' ORDER BY asof DESC, domain ASC, list ASC ';
+ }
+ $sql = 'SELECT xid FROM list '
+ . $this->_criteria
+ . $this->_order;
+ $q = $this->query($sql);
+ while ($q && (list($xid) = mysql_fetch_row($q))) {
+ $this->_xids[] = $xid;
+ $this->_mlists[] = $this->_dbo->mlist($xid);
+ }
+ }
+
+ function mlists() {
+ return $this->_mlists;
+ }
+
+ function xids() {
+ return $this->_xids;
+ }
+
+ function domains($countonly=false) {
+ $dlist = array();
+ foreach ($this->_mlists as $o_mlist) {
+ $dlist[$o_mlist->get('domain')] = true;
+ }
+ if ($countonly) {
+ return count($dlist);
+ }
+ ksort($dlist);
+ return array_keys($dlist);
+ }
+
+ function domain_count() {
+ return $this->domains(true);
+ }
+
+ function total_posts() {
+ $posts = 0;
+ foreach ($this->_mlists as $o_mlist) {
+ $n = $o_mlist->get('posts');
+ if ($n >= 0) {
+ $posts += $n;
+ }
+ }
+ return $posts;
+ }
+
+ function mean_posts() {
+ $days = $this->post_days();
+ if ($days) {
+ return $this->total_posts() / $days;
+ }
+ return 0;
+ }
+
+ function post_days() {
+ $days = 0;
+ foreach ($this->_mlists as $o_mlist) {
+ if ($o_mlist->get('posts') >= 0) {
+ $days++;
+ }
+ }
+ return $days;
+ }
+
+ function total_subscribers() {
+ $subs = 0;
+ foreach ($this->_mlists as $o_mlist) {
+ $n = $o_mlist->get('count');
+ if ($n >= 0) {
+ $subs += $n;
+ }
+ }
+ return $subs;
+ }
+
+ function total_digest_subscribers() {
+ $subs = 0;
+ foreach ($this->_mlists as $o_mlist) {
+ $n = $o_mlist->get('dcount');
+ if ($n >= 0) {
+ $subs += $n;
+ }
+ }
+ return $subs;
+ }
+
+}
+
+class lsMListSummary extends lsMList {
+
+ function lsMListSummary($dbo, $id=null,
+ $list=null, $from=null, $until=null) {
+ $this->id('xid', 'list_summary');
+ $this->importdb($dbo);
+ if (! isset($id)) {
+ $this->isNew(true);
+ if (isset($list) && isset($from)) {
+ $this->summarise($list, $from, $until);
+ }
+ return;
+ }
+ if (is_numeric($id)) {
+ $this->refresh((int)$id);
+ return;
+ }
+ }
+
+ function summarise($list, $begin, $end=null) {
+ $from = new rlib_Date($begin);
+ $until = new rlib_Date($end);
+ $from = $from->as_dbdate();
+ $until = $until->as_dbdate();
+
+ if ($until <= $from) {
+ return;
+ }
+ print "selecting\n";
+
+ $criteria = $this->where('list', $list)
+ . ' AND '
+ . $this->where('asof', $from, '>=')
+ . ' AND '
+ . $this->where('asof', $until, '<');
+ $o_selection = $this->_dbo->select($criteria);
+ $a_mlists = $o_selection->mlists();
+ $o_last = $a_mlists[count($a_mlists) - 1];
+ $this->set('list', $list);
+ $this->set('list_xid', $a_mlists[0]->get('xid'));
+ $this->set('interval_start', $from);
+ $this->set('interval_end', $until);
+ $n = count($o_selection->xids());
+ $subs = $o_selection->total_subscribers();
+ $this->set('count', $subs);
+ $this->set('mean_count', $subs / $n);
+ $subs = $o_selection->total_digest_subscribers();
+ $this->set('dcount', $subs);
+ $this->set('mean_dcount', $subs / $n);
+ $this->set('posts', $o_selection->total_posts());
+ $this->set('mean_posts', $o_selection->mean_posts());
+ }
+
+ function subscriber_count() {
+ return $this->get('count');
+ }
+ function digester_count() {
+ return $this->get('dcount');
+ }
+ function digest_subscriber_count() {
+ return $this->digester_count();
+ }
+
+ function info($entify=false) {
+ if (($iid = $this->get('info')) == 0) {
+ return null;
+ }
+ $o_info = $this->_dbo->infotext($iid);
+ $info = $o_info->get('info');
+ if ($entify) {
+ $info = HTMLentities($info);
+ }
+ return $info;
+ }
+
+}
+
+class lsInfoText extends lsDB {
+
+ function lsInfoText($dbo, $id=null) {
+ $this->id('xid', 'info_text');
+ $this->importdb($dbo);
+ if (! isset($id)) {
+ $this->isNew(true);
+ return;
+ }
+ if (is_numeric($id)) {
+ $this->refresh((int)$id);
+ return;
+ }
+ $sql = 'SELECT xid FROM info_text WHERE '
+ . $this->where('sum', $id);
+ $q = $this->query($sql);
+ if ($q && (mysql_num_rows($q) > 0)) {
+ list($xid) = mysql_fetch_row($q);
+ $this->refresh((int)$xid);
+ }
+ }
+}
+
+class lsMList extends lsDB {
+
+ function lsMList($dbo, $id=null, $asof=null) {
+ $this->id('xid', 'list');
+ $this->importdb($dbo);
+ if (! isset($id)) {
+ $this->isNew(true);
+ return;
+ }
+ if (is_numeric($id)) {
+ $this->refresh((int)$id);
+ return;
+ }
+ $sql = 'SELECT xid FROM list WHERE '
+ . $this->where('list', $id)
+ . (isset($asof) ? ' AND ' . $this->where('asof', $asof) : '')
+ . 'ORDER BY asof DESC LIMIT 1 ';
+ $q = $this->query($sql);
+ if ($q && (mysql_num_rows($q) > 0)) {
+ list($id) = mysql_fetch_row($q);
+ $this->refresh((int)$id);
+ return;
+ }
+ }
+
+ function subscriber_count() {
+ return $this->get('count');
+ }
+ function digester_count() {
+ return $this->get('dcount');
+ }
+ function digest_subscriber_count() {
+ return $this->digester_count();
+ }
+
+ function info($entify=false) {
+ if (($iid = $this->get('info')) == 0) {
+ return null;
+ }
+ $o_info = $this->_dbo->infotext($iid);
+ $info = $o_info->get('info');
+ if ($entify) {
+ $info = HTMLentities($info);
+ }
+ return $info;
+ }
+
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "bsd"
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+?>
Propchange: labs/pulse/report-web/liststats-classes.php
------------------------------------------------------------------------------
svn:executable = *
Added: labs/pulse/report-web/mlists.php
URL: http://svn.apache.org/viewvc/labs/pulse/report-web/mlists.php?view=auto&rev=498170
==============================================================================
--- labs/pulse/report-web/mlists.php (added)
+++ labs/pulse/report-web/mlists.php Sat Jan 20 13:21:18 2007
@@ -0,0 +1,409 @@
+<?php
+
+Header('text/html; charset=UTF-8');
+
+Include_Once('liststats-classes.php');
+$stderr = fopen('php://stderr', 'a');
+
+function safelise($list) {
+ $list = preg_replace('/@/', ' @ ', $list);
+ return $list;
+}
+
+function fragmentalise($list) {
+ $list = preg_replace('/@/', '_at_', $list);
+ return $list;
+}
+
+function chartit($o_db, $list, $width, $height) {
+ $image = ImageCreate($width, $height);
+ $margin = 40;
+ $left = $margin;
+ $right = $width - $margin;
+ $width -= $margin;
+ /*
+ * Messages
+ */
+ $red = ImageColorAllocate($image, 255, 0, 0);
+ $blue = ImageColorAllocate($image, 0, 0, 255);
+ $green = ImageColorAllocate($image, 0, 255, 0);
+ $grey = ImageColorAllocate($image, 127, 127, 127);
+ $white = ImageColorAllocate($image, 255, 255, 255);
+ $black = ImageColorAllocate($image, 0, 0, 0);
+
+ $colour_posts = $red;
+ $colour_subscribers = $blue;
+ /*
+ * Make the whole thing transparent (background)
+ */
+ ImageColorTransparent($image, $black);
+ ImageFill($image, 0, 0, $white);
+ /*
+ * Draw the border in grey
+ */
+ ImageLine($image, $left, 0, $left, $height - 1, $grey);
+ ImageLine($image, $left, $height - 1, $right - 1, $height - 1, $grey);
+ ImageLine($image, $right - 1, $height - 1, $right - 1, 0, $grey);
+ ImageLine($image, $right - 1, 0, $left, 0, $grey);
+
+ $o_points = $o_db->select(array('list' => $list,
+ 'posts' => array(0, '>=')),
+ 'asof ASC');
+ $a_points = $o_points->mlists();
+ if (! $a_points) {
+ $a_points = array();
+ }
+ $points = count($a_points);
+ if ($points) {
+ $x_int = ($width - $margin) / $points;
+ }
+ else {
+ $x_int = 0;
+ }
+
+ $highest = 0;
+ foreach ($a_points as $o_point) {
+ $highest = max($highest, $o_point->get('posts'));
+ }
+ if ($highest) {
+ $y_int = ($height * 0.90) / $highest;
+ }
+ else {
+ $y_int = 0;
+ }
+ $height_posts = $height - ($highest * $y_int);
+ $max_posts = $highest;
+
+ $last_x = $left;
+ if ($points) {
+ $last_y = $height - ($a_points[0]->get('posts') * $y_int);
+ }
+ else {
+ $last_y = 0;
+ }
+ for ($i = 0; $i < $points; $i++) {
+ $o_point = $a_points[$i];
+ $this_y = $o_point->get('posts') * $y_int;
+ $this_y = $height - $this_y;
+ ImageLine($image, $last_x, $last_y,
+ min($last_x + $x_int, $right - 1), $this_y,
+ $colour_posts);
+ $last_x += $x_int;
+ $last_y = $this_y;
+ }
+
+ /*
+ * Now do the subscribers
+ */
+ $highest = 0;
+ foreach ($a_points as $o_point) {
+ $highest = max($highest, $o_point->get('count'));
+ }
+ if ($highest) {
+ $y_int = ($height * 0.90) / $highest;
+ }
+ else {
+ $y_int = 0;
+ }
+ $height_subscribers = $height - ($highest * $y_int);
+ $max_subscribers = $highest;
+
+ $last_x = $left;
+ if ($points) {
+ $last_y = $height - ($a_points[0]->get('count') * $y_int);
+ }
+ else {
+ $last_y = 0;
+ }
+ for ($i = 0; $i < $points; $i++) {
+ $o_point = $a_points[$i];
+ $this_y = $height - ($o_point->get('count') * $y_int);
+ ImageLine($image, $last_x, $last_y,
+ min($last_x + $x_int, $right - 1), $this_y,
+ $colour_subscribers);
+ $last_x += $x_int;
+ $last_y = $this_y;
+ }
+ $height_subscribers = $height - ($highest * $y_int);
+
+ /*
+ * Draw the max-subscriber line across the graph
+ */
+ ImageLine($image, $left, $height_subscribers,
+ $right - 1, $height_subscribers, $grey);
+ ImageLine($image, 0, $height_subscribers,
+ $left, $height_subscribers, $colour_subscribers);
+ $string = (string)$max_subscribers;
+ $px = $left - 2 - (strlen($string) * ImageFontWidth(0));
+ ImageString($image, 0, $px, $height_subscribers + 3, $string,
+ $colour_subscribers);
+
+ ImageLine($image, $left, $height_posts,
+ $right - 1, $height_posts, $grey);
+ ImageLine($image, $right, $height_posts,
+ $right + $margin, $height_posts, $colour_posts);
+ $string = (string)$max_posts;
+ ImageString($image, 0, $right + 2, $height_posts + 3, $string,
+ $colour_posts);
+ return $image;
+}
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>The Apache Software Foundation Mailing Lists</title>
+ <link rel="StyleSheet" href="mlists-generic.css" type="text/css"
+ media="all"/>
+ </head>
+ <body>
+ <h1>The Apache Software Foundation Mailing Lists</h1>
+<?php
+$o_db = new lsDB('<dbfqhn>', '<username>', '<password>');
+$asof = $o_db->latest();
+$o_sel_current = $o_db->select(array('asof' => $asof,
+ 'status' => 'public'));
+$a_mlists = $o_sel_current->mlists();
+$a_domains = $o_sel_current->domains();
+$total_domains = count($a_domains);
+$total_lists = count($a_mlists);
+print " <p>\n"
+ . " This information is collected and analysed on a daily basis.\n"
+ . ' This report was generated at ' . strftime('%c', time()) . " and\n"
+ . " covers a total of $total_lists mailing list"
+ . ($total_lists == 1 ? ' ' : 's ')
+ . "across $total_domains domain"
+ . ($total_domains == 1 ? '' : 's')
+ . ".\n"
+ . " </p>\n"
+ . " <p>\n"
+ . " The graph alongside each list shows the gross activity over the\n"
+ . " last 90 days. The blue line\n"
+ . " represents the number of subscribers to the list, and the red\n"
+ . " line charts the number of posts <i>per</i> day. They are not\n"
+ . " to the same scale. <b>This graph is indicative only, showing\n"
+ . " trends, not exact counts.</b>\n"
+ . " </p>\n"
+ . " <menu>\n"
+ . " <li><a href=\"#statistics\">Statistical summaries</a></li>\n"
+ . " </menu>\n";
+
+if ($total_domains != 0) {
+ $per_cell = ceil($total_domains / 3);
+ print " <p>\n"
+ . " Jump to the lists for a domain:\n"
+ . " </p>\n"
+ . " <table class=\"TOC\">\n"
+ . " <tr valign=\"top\">\n";
+ for ($i = 1; $i <= $total_domains; $i++) {
+ if ((($i - 1) % $per_cell) == 0) {
+ if ($i > 1) {
+ print " </menu>\n"
+ . " </td>\n";
+ }
+ print " <td><menu>\n";
+ }
+ print ' <li><a href="#'
+ . fragmentalise($a_domains[$i - 1])
+ . '">' . $a_domains[$i - 1] . "</a></li>\n";
+ }
+ print " </menu>\n"
+ . " </td>\n"
+ . " </tr>\n"
+ . " </table>\n";
+}
+
+$domain = null;
+$a_posts = array();
+$a_meanposts = array();
+$a_subs = array();
+
+foreach ($a_mlists as $o_mlist) {
+ $cdomain = $o_mlist->get('domain');
+ if ($cdomain != $domain) {
+ if ($domain) {
+ fputs($stderr, "\n");
+ }
+ $domain = $cdomain;
+ print " <hr/>\n"
+ . ' <h2 id="' . fragmentalise($domain) . '">Domain '
+ . "<tt>$domain</tt></h2>\n";
+ $o_sel_indomain = $o_db->select(array('asof' => $asof,
+ 'status' => 'public',
+ 'domain' => $domain));
+ $a_scratch = $o_sel_indomain->xids();
+ $n = count($a_scratch);
+ print " <p>\n"
+ . " A total of $n list"
+ . ($n == 1 ? '' : 's')
+ . ".\n"
+ . " </p>\n";
+ }
+ $mlist = $o_mlist->get('list');
+ fputs($stderr, '#');
+ $image = chartit($o_db, $mlist, 468, 200);
+ $fname = preg_replace('/[.@]/', '_', $mlist) . '.png';
+ ImagePNG($image, $fname);
+ $o_sel_mlist = $o_db->select(array('list' => $o_mlist->get('list')));
+ $subs = $o_mlist->subscriber_count();
+ $digesters = $o_mlist->digester_count();
+ $a_subs[$mlist] = $subs + (isset($digesters) ? $digesters : 0);
+ $posts = $o_sel_mlist->total_posts();
+ $meanposts = sprintf('%.2f', $o_sel_mlist->mean_posts());
+ $a_meanposts[$mlist] = $meanposts;
+ $days = $o_sel_mlist->post_days();
+ $days .= ' day' . ($days == 1 ? '' : 's');
+ $info = $o_mlist->info(true);
+ if ($info) {
+ $list = $o_mlist->get('list');
+ $domani = $o_mlist->get('domain');
+ $list = str_replace("@$domain", '', $list);
+ $info = str_replace('<#l#>', $list, $info);
+ $info = str_replace('<#l#>', $list, $info);
+ $info = str_replace('<#h#>', $domain, $info);
+ $info = str_replace('<#h#>', $domain, $info);
+ /*
+ * Try to turn all URLs into links.
+ */
+ $re_uri = ('\b[-_A-Za-z0-9]+://[-.A-Za-z0-9]+(?::[0-9]+)?'
+ . '(?:/[^\s()^]*)?');
+ $info = preg_replace('§' . "($re_uri)" . '§',
+ '<a href="$1">$1</a>',
+ $info);
+ }
+ $a_posts[$mlist] = $o_mlist->get('posts');
+ print " <hr class=\"list\"/>\n"
+ . ' <h3 id="' . fragmentalise($mlist) . '">Mailing list '
+ . '<tt>' . safelise($mlist) . "</tt></h3>\n";
+ print " <img src=\"$fname\"\n"
+ . " width=\"468\" height=\"200\" style=\"float: right;\"\n"
+ . " alt=\"[Activity graph]\" />\n"
+ . " <p>\n"
+ . " Traffic statistics cover a total of $days.\n"
+ . " </p>\n"
+ . " <table>\n"
+ . " <tr>\n"
+ . " <td>Current subscribers:</td>\n"
+ . ' <td>' . $o_mlist->subscriber_count() . "</td>\n"
+ . " </tr>\n"
+ . " <tr>\n"
+ . " <td>Current digest subscribers:</td>\n"
+ . ' <td>'
+ . (isset($digesters)
+ ? $o_mlist->digester_count()
+ : '<i>No digest available</i>')
+ . "</td>\n"
+ . " </tr>\n"
+ . " <tr>\n"
+ . " <td>Total posts ($days):</td>\n"
+ . " <td>$posts</td>\n"
+ . " </tr>\n"
+ . " <tr>\n"
+ . " <td>Mean posts <i>per</i> day:</td>\n"
+ . " <td>$meanposts</td>\n"
+ . " </tr>\n"
+ . " </table>\n";
+ if (isset($info)) {
+ print " <dl>\n"
+ . ' <dt><b>Description provided by the list '
+ . "moderators:</b></dt>\n"
+ . " <dd>\n"
+ . " <table class=\"description\">\n"
+ . " <tr>\n"
+ . " <td><pre>$info</pre></td>\n"
+ . " </tr>\n"
+ . " </table></dd>\n"
+ . " </dl>\n";
+ }
+ print " <br clear=\"all\"/>\n";
+}
+fputs($stderr, "\n");
+?>
+ <hr class="list"/>
+ <h2 id="statistics">Statistical Summaries</h2>
+ <ol class="statistics">
+ <li>Top-10 busiest lists in the last collection period:
+ <table>
+ <tr>
+ <th>List</th>
+ <th>Total posts</th>
+ </tr>
+<?php
+arsort($a_posts);
+$n = 1;
+foreach ($a_posts as $mlist => $posts) {
+ $mlist = '<a href="#' . fragmentalise($mlist) . '"><tt>'
+ . safelise($mlist) . "</tt></a";
+ print " <tr>\n"
+ . " <td>$mlist</td>\n"
+ . " <td>$posts</td>\n"
+ . " </tr>\n";
+ if ($n++ > 10) {
+ break;
+ }
+}
+?>
+ </table></li>
+ <li>Top-10 historically busiest lists:
+ <table>
+ <tr>
+ <th>List</th>
+ <th>Posts/day</th>
+ </tr>
+<?php
+arsort($a_meanposts);
+$n = 1;
+foreach ($a_meanposts as $mlist => $posts) {
+ $mlist = '<a href="#' . fragmentalise($mlist) . '"><tt>'
+ . safelise($mlist) . "</tt></a";
+ print " <tr>\n"
+ . " <td>$mlist</td>\n"
+ . " <td>$posts</td>\n"
+ . " </tr>\n";
+ if ($n++ > 10) {
+ break;
+ }
+}
+?>
+ </table></li>
+ <li>Top-10 most popular lists (most subscribers) as of the last
+ collection period:
+ <table>
+ <tr>
+ <th>List</th>
+ <th>Subscribers</th>
+ </tr>
+<?php
+arsort($a_subs);
+$n = 1;
+foreach ($a_subs as $mlist => $subs) {
+ $mlist = '<a href="#' . fragmentalise($mlist) . '"><tt>'
+ . safelise($mlist) . "</tt></a";
+ print " <tr>\n"
+ . " <td>$mlist</td>\n"
+ . " <td>$subs</td>\n"
+ . " </tr>\n";
+ if ($n++ > 10) {
+ break;
+ }
+}
+?>
+ </table></li>
+ </ol>
+ <hr />
+ <p>
+ <i>Data collected, analysed, and presented using Perl, MySQL, PHP,
+ and XML.</i>
+ </p>
+ </body>
+</html>
+<?php
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "bsd"
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+?>
Propchange: labs/pulse/report-web/mlists.php
------------------------------------------------------------------------------
svn:executable = *
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@labs.apache.org
For additional commands, e-mail: commits-help@labs.apache.org