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('/@/', '&#x2009;&#x40;&#x2009;', $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('&lt;#l#&gt;', $list, $info);
+        $info = str_replace('<#h#>', $domain, $info);
+        $info = str_replace('&lt;#h#&gt;', $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