You are viewing a plain text version of this content. The canonical link for it is here.
Posted to general@gump.apache.org by aj...@apache.org on 2004/08/11 18:32:37 UTC

svn commit: rev 36230 - in gump/trunk: . bin cron log mysql python/gump python/gump/core python/gump/document python/gump/document/xdocs python/gump/java python/gump/model python/gump/mysql python/gump/notify python/gump/runner python/gump/shared python/gump/stats python/gump/stats/dbm python/gump/stats/mysql python/gump/utils test

Author: ajack
Date: Wed Aug 11 09:32:36 2004
New Revision: 36230

Added:
   gump/trunk/gump.py
   gump/trunk/log/   (props changed)
   gump/trunk/mysql/
   gump/trunk/mysql/gump.sql
   gump/trunk/python/gump/mysql/
   gump/trunk/python/gump/mysql/__init__.py
   gump/trunk/python/gump/mysql/data.py
   gump/trunk/python/gump/mysql/databaser.py
   gump/trunk/python/gump/stats/dbm/
   gump/trunk/python/gump/stats/dbm/__init__.py
   gump/trunk/python/gump/stats/dbm/statsdb.py
      - copied, changed from rev 35601, gump/trunk/python/gump/stats/statsdb.py
   gump/trunk/python/gump/stats/mysql/
   gump/trunk/python/gump/stats/mysql/__init__.py
   gump/trunk/python/gump/stats/mysql/statsdb.py
   gump/trunk/python/gump/utils/mysql.py
   gump/trunk/test/gumptest.bat
      - copied unchanged from rev 35601, gump/trunk/test/gumpytest.bat
   gump/trunk/test/gumptest.sh
      - copied, changed from rev 35601, gump/trunk/test/gumpytest.sh
Removed:
   gump/trunk/python/gump/stats/statsdb.py
   gump/trunk/test/gumpytest.bat
   gump/trunk/test/gumpytest.sh
Modified:
   gump/trunk/bin/redo.py
   gump/trunk/cron/gump.py
   gump/trunk/python/gump/   (props changed)
   gump/trunk/python/gump/core/config.py
   gump/trunk/python/gump/core/gumpinit.py
   gump/trunk/python/gump/document/documenter.py
   gump/trunk/python/gump/document/xdocs/documenter.py
   gump/trunk/python/gump/java/helper.py
   gump/trunk/python/gump/model/module.py
   gump/trunk/python/gump/model/project.py
   gump/trunk/python/gump/model/stats.py
   gump/trunk/python/gump/model/workspace.py
   gump/trunk/python/gump/notify/notifier.py
   gump/trunk/python/gump/runner/demand.py
   gump/trunk/python/gump/runner/runner.py
   gump/trunk/python/gump/shared/comparator.py
   gump/trunk/python/gump/stats/__init__.py
   gump/trunk/python/gump/stats/statistician.py
   gump/trunk/python/gump/utils/__init__.py
   gump/trunk/python/gump/utils/timing.py
   gump/trunk/python/gump/utils/work.py
Log:
1) Work on a root 'gump.py' script (like cron/gump.py but w/ output to screen)
2) Work on MySQL
3) Some work on DBM.
Probably broken w/ datetime changes for (2) will test on Linux when in SVN.
4) A fix for 'redo', so I could tinker on Ant|Mono

Modified: gump/trunk/bin/redo.py
==============================================================================
--- gump/trunk/bin/redo.py	(original)
+++ gump/trunk/bin/redo.py	Wed Aug 11 09:32:36 2004
@@ -59,7 +59,7 @@
     options.setText(True)
     
     # 
-    options.setObjectives(OBJECTIVE_REDO)    
+    options.setObjectives(gump.run.options.OBJECTIVE_REDO)    
     
     # The Run Details...
     run=GumpRun(workspace,ps,options)

Modified: gump/trunk/cron/gump.py
==============================================================================
Binary files. No diff available.

Added: gump/trunk/gump.py
==============================================================================
--- (empty file)
+++ gump/trunk/gump.py	Wed Aug 11 09:32:36 2004
@@ -0,0 +1,306 @@
+#!/usr/bin/env python
+
+#
+#   Copyright 2003-2004 The Apache Software Foundation
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# $Header: $
+
+"""
+
+  This is the commandline entrypoint into Python Gump,
+  used *primarily* by nightly cron jobs.
+  
+  It updates Gump (from CVS) to ensure it (itself) is 
+  latest, does some environment twiddling, and runs the
+  main gump/integration.py. Bit more twiddling with 
+  outputs afterwards...
+
+"""
+
+import os.path
+import os
+import sys
+import socket
+import time
+import signal
+import smtplib
+import StringIO
+from xml.dom import minidom
+
+LINE=' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - GUMP'
+
+GUMP_VERSION='2.0.2-alpha-0003'
+
+def runCommand(command,args='',dir=None,outputFile=None):
+    """ Run a command, and check the result... """
+    
+    #    
+    originalCWD=None
+    if dir:     
+        originalCWD=os.getcwd()
+        cwdpath=os.path.abspath(dir)
+        try:
+            sys.stdout.write('Executing with CWD: [' + dir + ']\n')    
+            if not os.path.exists(cwdpath): os.makedirs(dir)
+            os.chdir(cwdpath)
+        except Exception, details :
+            # Log the problem and re-raise
+            sys.stdout.write('Failed to create/change CWD [' + cwdpath + ']. Details: ' + str(details) + '\n')
+            return 0
+              
+    try:              
+        fullCommand = command + ' ' + args  
+        sys.stdout.write('Execute : ' + fullCommand + '\n')
+       
+        # Execute Command & Calculate Exit Code
+        systemReturn=os.system(fullCommand)
+        
+        if not os.name == 'dos' and not os.name == 'nt':
+            waitcode=systemReturn
+        
+            #
+            # The return code (from system = from wait) is (on Unix):
+            #
+            #    a 16 bit number
+            #    top byte    =    exit status
+            #    low byte    =    signal that killed it
+            #
+            exit_code=(((waitcode & 0xFF00) >> 8) & 0xFF)
+        
+        else:
+            exit_code=systemReturn
+    
+        if exit_code:
+            sys.stdout.write('Process Exit Code : ' + `exit_code` + '\n')
+    
+    finally:
+        if originalCWD: os.chdir(originalCWD)
+      
+    return exit_code
+
+def catFile(output,file,title=None):
+    """ Cat a file to a stream... """
+    if title:
+        output.write(LINE + '\n')    
+        output.write(title + '\n\n')
+        
+    input=open(file,'r')
+    line = input.readline()
+    while line:
+        output.write(line)
+        # Next...
+        line = input.readline()
+        
+def establishLock(lockFile):
+
+    failed=0
+    info=''
+    if 'posix'==os.name:
+        import fcntl
+                
+        try:            
+            lock=open(lockFile,'a+')
+            fcntl.flock(lock.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
+        except:            
+            failed=1
+            info=', and is locked.'
+        
+    else:
+        if os.path.exists(lockFile):
+            failed=1
+        
+        # Write this PID into a lock file
+        lock=open(lockFile,'w')
+            
+    if failed:
+        print """The lock file [%s] exists%s. 
+Either Gump is still running, or it terminated very abnormally.    
+Please resolve this (waiting or removing the lock file) before retrying.
+        """ % (lockFile, info)
+        sys.exit(1)
+    
+    # Leave a mark...
+    lock.write(`os.getpid()`)
+    lock.flush()
+        
+    return lock
+        
+def releaseLock(lock,lockFile):
+      
+    if 'posix'==os.name:
+        import fcntl            
+        try:
+            fcntl.flock(lockFile.fileno(), fcntl.LOCK_UN)
+        except:
+            pass
+    
+    # Close it, so we can dispose of it
+    lock.close()    
+    
+    # Others might be blocked on this
+    try:
+        os.remove(lockFile)
+    except:
+        # Somehow another could delete this, even if locked...
+        pass
+             
+# Ensure we start in the correct directory, setting GUMP_HOME
+gumpHome=os.path.abspath(os.getcwd())
+os.environ['GUMP_HOME']=gumpHome     
+os.chdir(gumpHome)
+
+# Allow a lock    
+lockFile=os.path.abspath('gump.lock')
+lock=establishLock(lockFile)        
+    
+hostname='Unknown'
+workspaceName='Unknown'
+        
+args=sys.argv
+result=0
+svnExit = -1
+cvsExit = -1
+integrationExit = -1
+        
+try:
+
+    try:
+        
+        # Process Environment
+        hostname = socket.gethostname()
+
+        sys.stdout.write('- ************************************\n')
+        sys.stdout.write('-            Apache Gump \n')
+        sys.stdout.write('- ************************************\n')
+        sys.stdout.write('- GUMP run on host   : ' + hostname + '\n')
+        sys.stdout.write('- GUMP run @         : ' + time.strftime('%d %b %y %H:%M:%S', time.localtime()) + '\n')
+        sys.stdout.write('- GUMP run @  UTC    : ' + time.strftime('%d %b %y %H:%M:%S', time.gmtime()) + '\n')
+        sys.stdout.write('- GUMP run by Python : ' + `sys.version` + '\n')
+        sys.stdout.write('- GUMP run by Python : ' + `sys.executable` + '\n')
+        sys.stdout.write('- GUMP run by Gump   : ' + GUMP_VERSION + '\n')
+        sys.stdout.write('- GUMP run on OS     : ' + `os.name` + '\n')
+        
+        #sys.stdout.write('- GUMP run in env    : \n')        
+        #for envkey in os.environ.keys():
+        #    envval=os.environ[envkey]
+        #    sys.stdout.write('      ' + envkey + ' -> [' + envval + ']\n')
+        
+        # Workspace is the hostname, unless overridden
+        workspaceName = 'metadata/' + hostname + '.xml'
+        if len(args)>2 and args[1] in ['-w','--workspace']:
+            workspaceName=args[2]
+            del args[1:3]     
+        workspacePath = os.path.abspath(workspaceName)
+            
+        projectsExpr='all'
+        if len(args)>1:
+            projectsExpr=args[1]
+            del args[1:2]      
+            
+        # Check version information
+        (major, minor, micro, releaselevel, serial) = sys.version_info
+        if not major >=2 and minor >= 3:
+            raise RuntimeError('Gump requires Python 2.3 or above. [' + sys.version() + ']')
+            
+        # Nope, can't find the workspace...
+        if not os.path.exists(workspacePath):
+            raise RuntimeError('No such workspace at ' + str(workspacePath))
+   
+        # Add Gump to Python Path...
+        pythonPath=''
+        if os.environ.has_key('PYTHONPATH'):
+            pythonPath=os.environ['PYTHONPATH']
+            pythonPath+=os.pathsep
+        pythonDir=str(os.path.abspath(os.path.join(os.getcwd(),'python')))
+        pythonPath+=pythonDir
+        sys.stdout.write('- GUMP PYTHONPATH  :  ' + pythonPath + '\n')
+        os.environ['PYTHONPATH']=pythonPath
+        
+        # Wipe all *.pyc from the pythonPath (so we don't
+        # have old code lying around as compiled zombies)
+        for root, dirs, files in os.walk(pythonDir):
+            for name in files:
+                if name.endswith('.pyc'):
+                    fullname=os.path.join(root, name)
+                    # sys.stdout.write('- Remove PYC : ' + fullname + '\n')    
+                    os.remove(fullname)       
+        
+        # Update Gump code from SVN
+        if not os.environ.has_key('GUMP_NO_SVN_UPDATE') and \
+            not os.environ.has_key('GUMP_NO_SCM_UPDATE'):
+            svnExit = runCommand('svn','update --non-interactive')
+        else:
+            sys.stdout.write('SVN update skipped per environment setting.\n')
+            svnExit=0
+        if svnExit:
+            result=1     
+        
+        if not result:
+            # Update Gump metadata from CVS
+            if not os.environ.has_key('GUMP_NO_CVS_UPDATE') and \
+                not os.environ.has_key('GUMP_NO_SCM_UPDATE'):
+                cvsroot=':pserver:anoncvs@cvs.apache.org:/home/cvspublic'
+                os.environ['CVSROOT']=cvsroot
+                # :TODO: ??? delete os.environ['CVS_RSH']
+                cvsExit = runCommand('cvs','-q update -dP','metadata')
+            else:
+                sys.stdout.write('CVS update skipped per environment setting.\n')
+                cvsExit=0
+            if cvsExit:
+                result=1
+            
+        # :TODO: Need to remove all *.pyc (other than this one)
+        # because a Gump refactor can leave old/stale compiled
+        # classes around.
+            
+        # :TODO: Is this a CVS thing, or a Gump historical thing?
+        if os.path.exists('.timestamp'): 
+            os.remove('.timestamp')            
+    
+        if not result:
+            # Process/build command line
+            iargs = '-w ' + workspaceName + ' ' + projectsExpr + ' ' + ' '.join(args[1:])
+            
+            # Allow a check not an integrate
+            check=0
+            if '--check' in args:
+                check=0
+            
+            #
+            # Run the main Gump...
+            #    
+            command='bin/integrate.py'
+            if check:
+                command='bin/check.py'
+            integrationExit = runCommand(sys.executable+ ' '+command, iargs)
+            if integrationExit:
+                result=1
+
+    except KeyboardInterrupt:    
+        sys.stdout.write('Terminated by user interrupt...\n')
+        result = 1
+        raise
+        
+    except:    
+        sys.stdout.write('Terminated unintentionally...\n')
+        result = 1
+        raise
+    
+finally:
+ 
+    releaseLock(lock,lockFile) 
+       
+# bye!
+sys.exit(result)

Added: gump/trunk/mysql/gump.sql
==============================================================================
--- (empty file)
+++ gump/trunk/mysql/gump.sql	Wed Aug 11 09:32:36 2004
@@ -0,0 +1,172 @@
+# Copyright 2003-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#     http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# Host: localhost
+# Database: gump
+# Table: 'gump_module'
+# 
+CREATE TABLE `gump_module` (
+  `module_name` varchar(100) NOT NULL default '',
+  `description` varchar(100) NOT NULL default '',
+  PRIMARY KEY  (`module_name`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
+
+# Host: localhost
+# Database: gump
+# Table: 'gump_module_run'
+# 
+CREATE TABLE `gump_module_run` (
+  `run_id` varchar(100) NOT NULL default '',
+  `module_name` varchar(100) NOT NULL default '',
+  `state` int(11) NOT NULL default '0',
+  `reason` int(11) NOT NULL default '0',
+  `cause` varchar(100) default '',
+  `start` datetime NOT NULL default '0000-00-00 00:00:00',
+  `end` datetime NOT NULL default '0000-00-00 00:00:00',
+  PRIMARY KEY  (`module_name`,`run_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
+
+# Host: localhost
+# Database: gump
+# Table: 'gump_module_stats'
+# 
+CREATE TABLE `gump_module_stats` (
+  `module_name` varchar(100) NOT NULL default '',
+  `successes` int(11) NOT NULL default '0',
+  `failures` int(11) NOT NULL default '0',
+  `prereqs` int(11) NOT NULL default '0',
+  `first` datetime default '0000-00-00 00:00:00',
+  `last` datetime default '0000-00-00 00:00:00',
+  `current_state` int(11) NOT NULL default '0',
+  `previous_state` int(11) NOT NULL default '0',
+  `start_of_state` datetime default '0000-00-00 00:00:00',
+  `sequence_in_state` int(11) NOT NULL default '0',
+  `last_modified` datetime NOT NULL default '0000-00-00 00:00:00',
+  PRIMARY KEY  (`module_name`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
+
+# Host: localhost
+# Database: gump
+# Table: 'gump_project'
+# 
+CREATE TABLE `gump_project` (
+  `project_name` varchar(100) NOT NULL default '',
+  `description` varchar(100) NOT NULL default '',
+  `module_name` varchar(100) NOT NULL default '',
+  PRIMARY KEY  (`project_name`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
+
+# Host: localhost
+# Database: gump
+# Table: 'gump_project_run'
+# 
+CREATE TABLE `gump_project_run` (
+  `run_id` varchar(100) NOT NULL default '',
+  `project_name` varchar(100) NOT NULL default '',
+  `state` int(11) NOT NULL default '0',
+  `reason` int(11) NOT NULL default '0',
+  `cause` varchar(100) default '',
+  `start` datetime NOT NULL default '0000-00-00 00:00:00',
+  `end` datetime NOT NULL default '0000-00-00 00:00:00',
+  PRIMARY KEY  (`run_id`,`project_name`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
+
+# Host: localhost
+# Database: gump
+# Table: 'gump_project_stats'
+# 
+CREATE TABLE `gump_project_stats` (
+  `project_name` varchar(100) NOT NULL default '',
+  `successes` int(11) NOT NULL default '0',
+  `failures` int(11) NOT NULL default '0',
+  `prereqs` int(11) NOT NULL default '0',
+  `first` datetime default '0000-00-00 00:00:00',
+  `last` datetime default '0000-00-00 00:00:00',
+  `current_state` int(11) NOT NULL default '0',
+  `previous_state` int(11) NOT NULL default '0',
+  `start_of_state` datetime default '0000-00-00 00:00:00',
+  `sequence_in_state` int(11) NOT NULL default '0',
+  PRIMARY KEY  (`project_name`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
+
+# Host: localhost
+# Database: gump
+# Table: 'gump_reason_code'
+# 
+CREATE TABLE `gump_reason_code` (
+  `code` int(11) NOT NULL default '0',
+  `name` varchar(100) NOT NULL default '',
+  `description` varchar(100) NOT NULL default '',
+  PRIMARY KEY  (`code`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
+
+# Host: localhost
+# Database: gump
+# Table: 'gump_repository_stats'
+# 
+CREATE TABLE `gump_repository_stats` (
+  `repository_name` varchar(100) NOT NULL default '',
+  `successes` int(11) NOT NULL default '0',
+  `failures` int(11) NOT NULL default '0',
+  `prereqs` int(11) NOT NULL default '0',
+  `first` datetime default '0000-00-00 00:00:00',
+  `last` datetime default '0000-00-00 00:00:00',
+  `current_state` int(11) NOT NULL default '0',
+  `previous_state` int(11) NOT NULL default '0',
+  `start_of_state` datetime default '0000-00-00 00:00:00',
+  `sequence_in_state` int(11) NOT NULL default '0',
+  PRIMARY KEY  (`repository_name`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
+
+# Host: localhost
+# Database: gump
+# Table: 'gump_run'
+# 
+CREATE TABLE `gump_run` (
+  `run_id` varchar(100) NOT NULL default '',
+  `start` datetime NOT NULL default '0000-00-00 00:00:00',
+  `end` datetime NOT NULL default '0000-00-00 00:00:00',
+  PRIMARY KEY  (`run_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
+
+# Host: localhost
+# Database: gump
+# Table: 'gump_state'
+# 
+CREATE TABLE `gump_state` (
+  `code` int(11) NOT NULL default '0',
+  `name` varchar(100) NOT NULL default '',
+  `description` varchar(100) NOT NULL default '',
+  PRIMARY KEY  (`code`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
+
+# Host: localhost
+# Database: gump
+# Table: 'gump_workspace_stats'
+# 
+CREATE TABLE `gump_workspace_stats` (
+  `workspace_name` varchar(100) NOT NULL default '',
+  `successes` int(11) NOT NULL default '0',
+  `failures` int(11) NOT NULL default '0',
+  `prereqs` int(11) NOT NULL default '0',
+  `first` datetime default '0000-00-00 00:00:00',
+  `last` datetime default '0000-00-00 00:00:00',
+  `current_state` int(11) NOT NULL default '0',
+  `previous_state` int(11) NOT NULL default '0',
+  `start_of_state` datetime default '0000-00-00 00:00:00',
+  `sequence_in_state` int(11) NOT NULL default '0',
+  PRIMARY KEY  (`workspace_name`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
+

Modified: gump/trunk/python/gump/core/config.py
==============================================================================
--- gump/trunk/python/gump/core/config.py	(original)
+++ gump/trunk/python/gump/core/config.py	Wed Aug 11 09:32:36 2004
@@ -69,7 +69,7 @@
     bannerimage = 'http://gump.apache.org/images/gump-logo.gif'
     
     email = 'gump@' + gumpfullhost
-    mailinglist = 'general@gump.apache.org'
+    administrator = 'general@gump.apache.org'
     mailserver = 'mail.apache.org'
     mailport = 25
     prefix = '[GUMP@' + gumphost + ']'

Modified: gump/trunk/python/gump/core/gumpinit.py
==============================================================================
--- gump/trunk/python/gump/core/gumpinit.py	(original)
+++ gump/trunk/python/gump/core/gumpinit.py	Wed Aug 11 09:32:36 2004
@@ -27,6 +27,7 @@
 import os.path
 import sys
 import time
+import datetime
 
 # tell Python what modules make up the gump package
 # __all__ = ["config"]
@@ -61,3 +62,8 @@
     default.date = time.strftime('%Y%m%d',default.ltime)
     default.datetime = time.strftime('%Y%m%d %H:%M:%S',default.ltime)
     
+    default.datetimeObject = datetime.datetime.fromtimestamp(default.time)
+
+    
+if __name__ == '__main__':
+    gumpinit()
\ No newline at end of file

Modified: gump/trunk/python/gump/document/documenter.py
==============================================================================
--- gump/trunk/python/gump/document/documenter.py	(original)
+++ gump/trunk/python/gump/document/documenter.py	Wed Aug 11 09:32:36 2004
@@ -72,7 +72,6 @@
         
         self.documentRun()
         
-
     def setResolver(self,resolver):
         self.resolver=resolver        
 

Modified: gump/trunk/python/gump/document/xdocs/documenter.py
==============================================================================
--- gump/trunk/python/gump/document/xdocs/documenter.py	(original)
+++ gump/trunk/python/gump/document/xdocs/documenter.py	Wed Aug 11 09:32:36 2004
@@ -567,7 +567,7 @@
         if not self.workspace.private:
             detailsTable.createEntry("E-mail Server: ", self.workspace.mailserver)
             detailsTable.createEntry("E-mail Port: ", self.workspace.mailport)
-            detailsTable.createEntry("List Address: ", self.workspace.mailinglist)
+            detailsTable.createEntry("List Address: ", self.workspace.administrator)
             detailsTable.createEntry("E-mail Address: ", self.workspace.email)            
             detailsTable.createEntry("Prefix: ", self.workspace.prefix)
             detailsTable.createEntry("Signature: ", self.workspace.signature)
@@ -1951,13 +1951,15 @@
             
         statsTable.createEntry("Current State: ", stateDescription(stats.currentState))
         statsTable.createEntry("Duration in state: ", stats.sequenceInState)
-        statsTable.createEntry("Start of state: ", secsToDateTime(stats.startOfState))
-        statsTable.createEntry("Previous State: ", stateDescription(stats.previousState))
+        if stats.startOfState:
+            statsTable.createEntry("Start of state: ", stats.startOfState.isoformat())
+        if stats.previousState:
+            statsTable.createEntry("Previous State: ", stats.previousState.isoformat())
         
         if stats.first:
-            statsTable.createEntry("First Success: ", secsToDateTime(stats.first))
+            statsTable.createEntry("First Success: ", stats.first.isoformat())
         if stats.last:
-            statsTable.createEntry("Last Success: ", secsToDateTime(stats.last))
+            statsTable.createEntry("Last Success: ", stats.last.isoformat())
             
     def displayClasspath(self,document,classpath,title,referencingObject):
         

Modified: gump/trunk/python/gump/java/helper.py
==============================================================================
--- gump/trunk/python/gump/java/helper.py	(original)
+++ gump/trunk/python/gump/java/helper.py	Wed Aug 11 09:32:36 2004
@@ -210,9 +210,6 @@
         if ids:
             for id in ids:
                 if not id in projectIds:
-                    # :TODO: This will cause repeats of this message
-                    # for every dep who tries to use this
-                    # Gumpy really needs to be OO!!!!
                     dependency.getOwnerProject().addWarning("Invalid ID [" + id \
                           + "] for dependency on [" + project.getName() + "]")
 

Modified: gump/trunk/python/gump/model/module.py
==============================================================================
--- gump/trunk/python/gump/model/module.py	(original)
+++ gump/trunk/python/gump/model/module.py	Wed Aug 11 09:32:36 2004
@@ -399,7 +399,7 @@
         # Grab all notifications
         for notifyEntry in self.getDomChildIterator('nag'):
             # Determine where to send
-            toaddr=getDomAttributeValue(notifyEntry,'to',workspace.mailinglist)
+            toaddr=getDomAttributeValue(notifyEntry,'to',workspace.administrator)
             fromaddr=getDomAttributeValue(notifyEntry,'from',workspace.email)   
             self.notifys.append(
                     AddressPair(
@@ -664,7 +664,7 @@
     """
     def __init__(self,moduleName):
         Statistics.__init__(self,moduleName)    
-        self.lastModified=-1        
+        self.lastModified=None
         
     def getLastModified(self):
         return (self.lastModified)
@@ -674,7 +674,7 @@
         
     def lastModifiedKey(self):
         return self.getKeyBase() + '-last-updated'
-
+        
     def update(self,module):      
         Statistics.update(self,module)
 

Modified: gump/trunk/python/gump/model/project.py
==============================================================================
--- gump/trunk/python/gump/model/project.py	(original)
+++ gump/trunk/python/gump/model/project.py	Wed Aug 11 09:32:36 2004
@@ -459,7 +459,7 @@
         # Grab all notifications
         for notifyEntry in self.getDomChildIterator('nag'):
             # Determine where to send
-            toaddr=getDomAttributeValue(notifyEntry,'to',workspace.mailinglist)
+            toaddr=getDomAttributeValue(notifyEntry,'to',workspace.administrator)
             fromaddr=getDomAttributeValue(notifyEntry,'from',workspace.email)   
             self.notifys.append(
                     AddressPair(

Modified: gump/trunk/python/gump/model/stats.py
==============================================================================
--- gump/trunk/python/gump/model/stats.py	(original)
+++ gump/trunk/python/gump/model/stats.py	Wed Aug 11 09:32:36 2004
@@ -22,7 +22,6 @@
 import os
 import sys
 import logging
-import anydbm
 
 import gump
 from gump.core.config import *
@@ -41,13 +40,12 @@
         self.successes=0
         self.failures=0
         self.prereqs=0
-        self.first=-1
-        self.last=-1
+        self.first=None
+        self.last=None
         self.currentState=STATE_UNSET
         self.previousState=STATE_UNSET
-        self.startOfState=-1        
+        self.startOfState=None
         self.sequenceInState=0
-        self.lastModified=0
                 
       
     # FOG is (at present) effectively the
@@ -89,9 +87,6 @@
     def lastKey(self):
         return self.getKeyBase() + '-last'
         
-    def lastModifiededKey(self):
-        return self.getKeyBase() + '-last-updated'
-        
     def currentStateKey(self):
         return self.getKeyBase() + '-current-state'
         
@@ -111,7 +106,7 @@
         if statable.isSuccess():
 
             self.successes += 1
-            self.last = default.time
+            self.last = default.datetimeObject
             
             # A big event...
             if not self.first:
@@ -140,13 +135,12 @@
             self.sequenceInState += 1            
         else:
             self.previousState=lastCurrentState  
-            self.startOfState = default.time                
+            self.startOfState = default.datetimeObject       
             self.sequenceInState = 1
            
     def dump(self, indent=0, output=sys.stdout):
         gump.utils.dump(self)
              
-            
 class Statable:
     def __init__(self): pass
     

Modified: gump/trunk/python/gump/model/workspace.py
==============================================================================
--- gump/trunk/python/gump/model/workspace.py	(original)
+++ gump/trunk/python/gump/model/workspace.py	Wed Aug 11 09:32:36 2004
@@ -30,7 +30,7 @@
 from gump.model.module import Module
 from gump.model.project import Project, ProjectSummary
 from gump.model.profile import Profile
-from gump.model.object import NamedModelObject
+from gump.model.object import NamedModelObject,ModelObject
 from gump.model.misc import Resultable
 from gump.model.property import PropertyContainer
 from gump.model.stats import Statable, Statistics
@@ -39,6 +39,32 @@
 
 import gump.core.config
 
+class DatabaseInformation(ModelObject):
+    def __init__(self,dom):    
+        ModelObject.__init__(self,dom)  
+        
+        # Some defaults...
+        self.host='localhost'
+        self.user='root'
+        self.passwd=''
+        self.database='gump'
+        
+    def complete(self,workspace): 
+        if self.isComplete(): return
+        
+        # In case we care
+        self.workspace=workspace
+        
+        # Import DOM attributes into self as attributes
+        transferDomInfo(self.element, self, {})   
+        
+        self.setComplete()
+        
+    def getHost(self): return self.host
+    def getUser(self): return self.user
+    def getPasswd(self): return self.passwd
+    def getDatabase(self): return self.database
+        
 class Workspace(NamedModelObject, PropertyContainer, Statable, Resultable):
     """
             
@@ -53,7 +79,6 @@
     	Statable.__init__(self)
     	Resultable.__init__(self)
     		
-    	#
     	# Named repositories (e.g. CVS,SVN,etc.)
     	# Named modules
     	# Named projects
@@ -71,6 +96,9 @@
         self.updaters=0
         self.builders=0
                     
+        # Database Informaton
+        self.dbInfo=None
+        
         # Set times
         self.initializeTimes()
 
@@ -236,6 +264,12 @@
     def getSortedProjects(self):
         return self.sortedProjects       
         
+    def hasDatabaseInformation(self):
+        if self.dbInfo: return True
+        return False
+        
+    def getDatabaseInformation(self):
+        return self.dbInfo
         
     def isMultithreading(self):
         return self.hasUpdaters() or self.hasBuilders()
@@ -272,7 +306,7 @@
         
         self.private=False
         self.email = default.email     
-        self.mailinglist = default.mailinglist  
+        self.administrator = default.administrator  
         self.mailserver = default.mailserver
         self.mailport = int(default.mailport)
         self.prefix=default.prefix
@@ -353,6 +387,10 @@
                 self.updaters=int(getDomAttributeValue(threads,'updaters'))
             if hasDomAttribute(threads,'builders'):
                 self.builders=int(getDomAttributeValue(threads,'builders'))
+                
+        if self.hasDomChild('database'):
+            self.dbInfo=DatabaseInformation(self.getDomChild('database'))
+            self.dbInfo.complete(self)
                                                              
         # Complete the properies
         self.completeProperties()

Added: gump/trunk/python/gump/mysql/__init__.py
==============================================================================
--- (empty file)
+++ gump/trunk/python/gump/mysql/__init__.py	Wed Aug 11 09:32:36 2004
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+
+# Copyright 2003-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#     http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+    Gump Database Interfacing
+"""

Added: gump/trunk/python/gump/mysql/data.py
==============================================================================
--- (empty file)
+++ gump/trunk/python/gump/mysql/data.py	Wed Aug 11 09:32:36 2004
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+
+# Copyright 2003-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#     http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from gump import log
+
+import MySQLdb
+import MySQLdb.cursors
+
+def configureState(conn):
+    """
+    Install the appropriate state codes.
+    """
+    from gump.model.state import stateNames,stateDescriptions
+    for (code, name) in stateNames.items():
+        try: 
+            cursor = conn.cursor()
+            cursor.execute("INSERT INTO gump.gump_state (code, name, description) VALUES (%s, '%s', '%s')"  % \
+                            (code, name, stateDescriptions[code]) )
+            cursor.close() 
+        except:
+            log.warn('Failed to load (%s -> %s)' % (code, name) )
+        
+    cursor = conn.cursor()
+    cursor.execute("SELECT * FROM gump.gump_state")
+    rows = cursor.fetchall()    
+    for row in rows:
+        print row['code'], row['name'], row['description']
+    cursor.close() 
+ 
+def configureReasonCode(conn):
+    """
+    Install the appropriate reason codes.
+    """    
+    from gump.model.state import reasonCodeNames,reasonCodeDescriptions
+    for (code, name) in reasonCodeNames.items():
+        try: 
+            cursor = conn.cursor()
+            cursor.execute("INSERT INTO gump.gump_reason_code (code, name, description) VALUES (%s, '%s', '%s')"  % \
+                            (code, name, reasonCodeDescriptions[code]) )
+            cursor.close() 
+        except:
+            log.warn('Failed to load (%s -> %s)' % (code, name) )
+        
+    cursor = conn.cursor()
+    cursor.execute("SELECT * FROM gump.gump_reason_code")
+    rows = cursor.fetchall()    
+    for row in rows:
+        print row['code'], row['name'], row['description']
+    cursor.close() 
+     
+    
+def configureDB():   
+    conn = MySQLdb.Connect(
+        host='localhost', user='root',
+        passwd='', db='gump',compress=1,
+        cursorclass=MySQLdb.cursors.DictCursor)
+        
+    configureState(conn)
+    configureReasonCode(conn)
+    
+    # Done
+    conn.close()
+        
+if __name__ == '__main__':
+    configureDB()
\ No newline at end of file

Added: gump/trunk/python/gump/mysql/databaser.py
==============================================================================
--- (empty file)
+++ gump/trunk/python/gump/mysql/databaser.py	Wed Aug 11 09:32:36 2004
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+
+# Copyright 2003-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#     http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+    Stash in Database
+"""
+
+import time
+import os
+import sys
+
+import MySQLdb
+import MySQLdb.cursors
+
+from gump import log
+from gump.run.gumprun import *
+import gump.run.actor
+
+class Databaser(gump.run.actor.AbstractRunActor):
+    
+    def __init__(self,run):              
+        gump.run.actor.AbstractRunActor.__init__(self,run)    
+        
+    def processOtherEvent(self,event):   
+        pass
+                      
+    def processWorkspace(self):
+        """
+        Add information about the workspace to the database 
+        """
+        pass
+    
+    def processModule(self,module):    
+        """
+        Add information about the module to the database
+        """
+        conn=None
+        helper=None
+        try:
+            conn=self.getConnected()
+            helper=gump.utils.mysql.DbHelper(conn)
+            
+            # Prepare the data
+            settings = {}
+            
+            settings['run_id'] = "'" + self.run.getRunHexGuid() + "'"  
+            settings['module_name']="'" + module.getName() + "'"
+            settings['state']=module.getState()
+            settings['reason']=module.getReason()
+            
+            if module.hasCause():
+                settings['cause']="'" + module.getCause().getName() + "'"
+                
+            #settings['start']=
+            #settings['end']=
+            
+            helper.insert('module_run',settings)
+            
+        finally:
+            if conn: conn.close()
+    
+    def processProject(self,project):    
+        """
+        Add information about the project to the database 
+        """
+        conn=None
+        helper=None
+        try:
+            conn=self.getConnected()
+            helper=gump.utils.mysql.DbHelper(conn)
+            
+            # Prepare the data
+            settings = {}
+            
+            settings['run_id'] = "'" + self.run.getRunHexGuid() + "'"
+            settings['project_name']="'" + project.getName() + "'"
+            settings['state']=project.getState()
+            settings['reason']=project.getReason()
+            
+            if project.hasCause():
+                settings['cause']="'" + project.getCause().getName() + "'"
+                
+            #settings['start']=
+            #settings['end']=
+            
+            helper.insert('project_run',settings)
+            
+        finally:
+            if conn: conn.close()
+        
+        
+    def getConnected(self):
+        """
+        Get a database connection.
+        """
+        dbInfo=self.workspace.getDatabaseInformation()
+                
+        return MySQLdb.Connect(
+                    host=dbInfo.getHost(), 
+                    user=dbInfo.getUser(),
+                    passwd=dbInfo.getPasswd(), 
+                    db=dbInfo.getDatabase(),
+                    compress=1,
+                    cursorclass=MySQLdb.cursors.DictCursor)
+        
+           
\ No newline at end of file

Modified: gump/trunk/python/gump/notify/notifier.py
==============================================================================
--- gump/trunk/python/gump/notify/notifier.py	(original)
+++ gump/trunk/python/gump/notify/notifier.py	Wed Aug 11 09:32:36 2004
@@ -97,7 +97,7 @@
         if self._hasUnwanted():
             log.info('We have some unwanted\'s to send to list...')
             
-            self.sendEmail(wsTo or self.workspace.mailinglist,wsFrom or self.workspace.email,
+            self.sendEmail(wsTo or self.workspace.administrator,wsFrom or self.workspace.email,
                         'BATCH: All dressed up, with nowhere to go...',
                         self._getUnwantedContent())
                         
@@ -112,7 +112,7 @@
         # Belt and braces (notify to us if not notify to them)
         if self._hasUnsent():
             log.info('We have some unsented\'s to send to list...')    
-            self.sendEmail(wsTo or self.workspace.mailinglist,wsFrom or self.workspace.email,
+            self.sendEmail(wsTo or self.workspace.administrator,wsFrom or self.workspace.email,
                         'BATCH: Unable to send...',
                          self._getUnsentContent())
                         
@@ -258,7 +258,7 @@
         
         subject=self.workspace.prefix+': Gump Workspace ' + self.workspace.getName()
         
-        self.sendEmail(self.workspace.mailinglist,
+        self.sendEmail(self.workspace.administrator,
                         self.workspace.email,
                         subject,content)
     

Modified: gump/trunk/python/gump/runner/demand.py
==============================================================================
--- gump/trunk/python/gump/runner/demand.py	(original)
+++ gump/trunk/python/gump/runner/demand.py	Wed Aug 11 09:32:36 2004
@@ -20,7 +20,6 @@
 	The OnDemand runner performs a run, but does work on
 	modules as the needs of projects demands it.
 
-
 """
 
 import os.path

Modified: gump/trunk/python/gump/runner/runner.py
==============================================================================
--- gump/trunk/python/gump/runner/runner.py	(original)
+++ gump/trunk/python/gump/runner/runner.py	Wed Aug 11 09:32:36 2004
@@ -113,9 +113,7 @@
     
     def initializeActors(self):
         """
-        
         Install the appropriate actors..
-        
         """
         
         # Stamp times
@@ -129,7 +127,17 @@
         if self.run.getOptions().isResults() and \
             self.run.getWorkspace().hasMultiplePythonServers():
             self.run.registerActor(Resulter(self.run))            
-              
+
+        # Add Database storer
+        if self.run.getOptions().isOfficial() and \
+            self.run.getWorkspace().hasDatabaseInformation():
+            try:
+                import gump.mysql.databaser
+                self.run.registerActor(gump.mysql.databaser.Databaser(self.run))
+            except Exception, details:
+                log.warning('Unable to register Database Actor :  %s ' % details,
+                            exc_info=1)
+        
         # Document..
         # Use XDOCS if not overridden...
         documenter=None

Modified: gump/trunk/python/gump/shared/comparator.py
==============================================================================
--- gump/trunk/python/gump/shared/comparator.py	(original)
+++ gump/trunk/python/gump/shared/comparator.py	Wed Aug 11 09:32:36 2004
@@ -63,6 +63,8 @@
 def compareModulesByLastModified(module1,module2):
     lu1=module1.getLastModified()
     lu2=module2.getLastModified()
+    if not lu1: lu1 = -1
+    if not lu2: lu2 = -1
     c= int(round(lu2 - lu1,0))                  
     if not c: c=cmp(module1,module2)
     return c             
@@ -105,6 +107,8 @@
 def compareProjectsByLastModified(project1,project2):
     lu1=project1.getLastModified()
     lu2=project2.getLastModified()
+    if not lu1: lu1 = -1
+    if not lu2: lu2 = -1
     c= int(round(lu2 - lu1,0))                  
     if not c: c=cmp(project1,project2)
     return c              

Modified: gump/trunk/python/gump/stats/__init__.py
==============================================================================
--- gump/trunk/python/gump/stats/__init__.py	(original)
+++ gump/trunk/python/gump/stats/__init__.py	Wed Aug 11 09:32:36 2004
@@ -15,4 +15,4 @@
 # limitations under the License.
 
 # tell Python what modules make up the gump.stats package
-__all__ = ["statsdb","statistician"]
+__all__ = ["statsdb","statistician","dbm","mysql"]

Added: gump/trunk/python/gump/stats/dbm/__init__.py
==============================================================================
--- (empty file)
+++ gump/trunk/python/gump/stats/dbm/__init__.py	Wed Aug 11 09:32:36 2004
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+
+# Copyright 2003-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#     http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# tell Python what modules make up the gump.shared package
+__all__ = ["statsdb"]

Copied: gump/trunk/python/gump/stats/dbm/statsdb.py (from rev 35601, gump/trunk/python/gump/stats/statsdb.py)
==============================================================================
--- gump/trunk/python/gump/stats/statsdb.py	(original)
+++ gump/trunk/python/gump/stats/dbm/statsdb.py	Wed Aug 11 09:32:36 2004
@@ -17,6 +17,7 @@
 """
 
 import time
+import datetime
 import os
 import sys
 import logging
@@ -52,99 +53,89 @@
         else:
             self.db={}
  
-    def dumpProjects(self):
-        """
-        Show all that is there
-        """
-        for key in self.db.keys():
-            if not -1 == key.find('-pname'):
-                pname=key[0:len(key)-6]
-                print "Project " + pname + " Key " + key
-                s=self.getProjectStats(pname)
-                dump(s)
                 
     # Workspace
     def getWorkspaceStats(self,workspaceName):
         stats=WorkspaceStatistics(workspaceName)
-        self.getBaseStats(stats)
+        self._getBaseStats(stats)
         return stats
         
     def putWorkspaceStats(self,stats):
-        self.putBaseStats(stats)
+        self._putBaseStats(stats)
 
     def delWorkspaceStats(self,stats):
-        self.delBaseStats(stats)          
+        self._delBaseStats(stats)          
         
         
     # Project
     
     def getProjectStats(self,projectName):
         stats=ProjectStatistics(projectName)        
-        self.getBaseStats(stats)
+        self._getBaseStats(stats)
         return stats
                 
     def putProjectStats(self,stats):
-        self.putBaseStats(stats)
+        self._putBaseStats(stats)
 
     def delProjectStats(self,stats):
-        self.delBaseStats(stats)          
+        self._delBaseStats(stats)          
 
     # Repository
     
     def getRepositoryStats(self,projectName):
         stats=RepositoryStatistics(projectName)        
-        self.getBaseStats(stats)
+        self._getBaseStats(stats)
         return stats
                 
     def putRepositoryStats(self,stats):
-        self.putBaseStats(stats)
+        self._putBaseStats(stats)
 
     def delRepositoryStats(self,stats):
-        self.delBaseStats(stats)          
+        self._delBaseStats(stats)          
 
     # Module
     
     def getModuleStats(self,moduleName):
         stats=ModuleStatistics(moduleName)        
-        self.getBaseStats(stats)
-        stats.lastModified=self.getDate(stats.lastModifiedKey())
+        self._getBaseStats(stats)
+        stats.lastModified=self._getDate(stats.lastModifiedKey())
         return stats
                 
     def putModuleStats(self,stats):
-        self.putBaseStats(stats)
-        self.putDate(stats.lastModifiedKey(), stats.lastModified)
+        self._putBaseStats(stats)
+        self._putDate(stats.lastModifiedKey(), stats.lastModified)
 
     def delModuleStats(self,stats):
-        self.delBaseStats(stats)
+        self._delBaseStats(stats)
         try:
             del self.db[stats.lastModifiedKey()]
         except:
             """ Hopefully means it wasn't there... """                
 
-    def getBaseStats(self,stats):
-        stats.successes=self.getInt(stats.successesKey())
-        stats.failures=self.getInt(stats.failuresKey())
-        stats.prereqs=self.getInt(stats.prereqsKey())
-        stats.first=self.getDate(stats.firstKey())
-        stats.last=self.getDate(stats.lastKey())
-        stats.currentState=stateForName(self.get(stats.currentStateKey()))
-        stats.previousState=stateForName(self.get(stats.previousStateKey()))
-        stats.startOfState=self.getDate(stats.startOfStateKey())
-        stats.sequenceInState=self.getInt(stats.sequenceInStateKey())
-        
-    def putBaseStats(self,stats):        
-        self.put(stats.nameKey(), stats.name)
-        self.putInt(stats.successesKey(), stats.successes)
-        self.putInt(stats.failuresKey(), stats.failures)
-        self.putInt(stats.prereqsKey(), stats.prereqs)
-        self.putDate(stats.firstKey(), stats.first)
-        self.putDate(stats.lastKey(), stats.last)
-        self.put(stats.currentStateKey(), stateName(stats.currentState))
-        self.put(stats.previousStateKey(), stateName(stats.previousState))
-        self.putDate(stats.startOfStateKey(), stats.startOfState)
-        self.putInt(stats.sequenceInStateKey(), stats.sequenceInState)
+    def _getBaseStats(self,stats):
+        stats.successes=self._getInt(stats.successesKey())
+        stats.failures=self._getInt(stats.failuresKey())
+        stats.prereqs=self._getInt(stats.prereqsKey())
+        stats.first=self._getDate(stats.firstKey())
+        stats.last=self._getDate(stats.lastKey())
+        stats.currentState=stateForName(self._get(stats.currentStateKey()))
+        stats.previousState=stateForName(self._get(stats.previousStateKey()))
+        stats.startOfState=self._getDate(stats.startOfStateKey())
+        stats.sequenceInState=self._getInt(stats.sequenceInStateKey())
+        
+    def _putBaseStats(self,stats):        
+        self._put(stats.nameKey(), stats.name)
+        self._putInt(stats.successesKey(), stats.successes)
+        self._putInt(stats.failuresKey(), stats.failures)
+        self._putInt(stats.prereqsKey(), stats.prereqs)
+        self._putDate(stats.firstKey(), stats.first)
+        self._putDate(stats.lastKey(), stats.last)
+        self._put(stats.currentStateKey(), stateName(stats.currentState))
+        self._put(stats.previousStateKey(), stateName(stats.previousState))
+        self._putDate(stats.startOfStateKey(), stats.startOfState)
+        self._putInt(stats.sequenceInStateKey(), stats.sequenceInState)
 
-    def delBaseStats(self,stats):
+    def _delBaseStats(self,stats):
         """
         Store the common stats
         """
@@ -189,19 +180,19 @@
         except:
             """ Hopefully means it wasn't there... """
         
-    def get(self,key):
+    def _get(self,key):
         key=str(key)
         val=''
         if self.db.has_key(key): val=str(self.db[key])
         return val
         
-    def getInt(self,key):
+    def _getInt(self,key):
         key=str(key)
         val=0
         if self.db.has_key(key): val=int(self.db[key])
         return val
         
-    def getFloat(self,key):
+    def _getFloat(self,key):
         """
         Get a float from the DB
         """
@@ -210,125 +201,40 @@
         if self.db.has_key(key): val=float(self.db[key])
         return val
         
-    def getDate(self,key):
+    def _getDate(self,key):
         """
         Get a date from the DB
         """
-        dateF=self.getFloat(key)
+        dateF=self._get(key)
         
-        # Hack to patch values incorrectly set to 0
-        # not -1 by default.
-        if 0 == dateF:
-            dateF = -1
+        if dateF:
+            print 'dateF ' + dateF
+            if not ' ' in dateF:
+                # Historical float perhaps?
+                date=datetime.datetime.utcfromtimestamp(int(dateF))
+            else:
+                date=datetime.datetime.utcfromtimestamp(time.mktime(time.strptime('%Y-%m-%d %H:%M:%S')))
+        else:
+            date=None
             
-        return dateF
+        return date
         
-    def put(self,key,val=''):
+    def _put(self,key,val=''):
         self.db[str(key)]=val
         
-    def putInt(self,key,val=0):
+    def _putInt(self,key,val=0):
         """
         Store an int
         """
         self.db[str(key)]=str(val)
         
-    def putDate(self,key,val=-1):
+    def _putDate(self,key,val):
         """
-        Store a date (as an int)
-        """
-        self.putInt(str(key),val)
-        
-    def loadStatistics(self,workspace):
+        Store a date (in iso format)
         """
-        
-        Load statistics from the DB onto the objects, so they can
-        reference the latest information (e.g. to set -debug)
-        
-        """
-        log.debug('--- Loading Statistics')
-                  
-                        
-        # Load the W/S statistics
-        ws=self.getWorkspaceStats(workspace.getName())
-        workspace.setStats(ws)            
-                
-        for repo in workspace.getRepositories():
-                        
-            # Load the statistics
-            rs=self.getRepositoryStats(repo.getName())
-                
-            # Stash for later...
-            repo.setStats(rs)    
-                      
-        for module in workspace.getModules():
-            # Load the statistics...
-            ms=self.getModuleStats(module.getName())        
-                
-            # Stash for later...
-            module.setStats(ms)     
-            
-            for project in module.getProjects():
-                # Load the statistics...
-                ps=self.getProjectStats(project.getName())        
-                
-                # Stash for later...
-                project.setStats(ps)            
-            
-    def updateStatistics(self,workspace):
-        """
-        
-        Go through the tree updating statistics as you go...
-        
-        """
-        log.debug('--- Updating Statistics')
-        
-        # Load the W/S statistics
-        ws=self.getWorkspaceStats(workspace.getName())
-        # Update for this workspace based off this run
-        ws.update(workspace)
-        workspace.setStats(ws)            
-                
-        for repo in workspace.getRepositories():
-                        
-            # Load the statistics
-            rs=self.getRepositoryStats(repo.getName())
-            
-            # Update for this repo based off this run
-            rs.update(repo)
-                
-            # Stash for later...
-            repo.setStats(rs)    
-              
-        for module in workspace.getModules():
-                        
-            # Load the statistics
-            ms=self.getModuleStats(module.getName())
-            
-            # Update for this project based off this run
-            ms.update(module)
-                
-            # Stash for later...
-            module.setStats(ms)            
-                
-            # Write out the updates
-            self.putModuleStats(ms)     
-            
-            for project in module.getProjects():
-                
-                # Load the statistics
-                ps=self.getProjectStats(project.getName())
-            
-                # Update for this project based off this run
-                ps.update(project)
-                
-                # Stash for later...
-                project.setStats(ps)            
-                
-                # Write out the updates
-                self.putProjectStats(ps) 
-                
-        self.sync()
-        
+        if val:
+            self._put(str(key),val.strftime('%Y-%m-%d %H:%M:%S'))
+
     def sync(self):
         """
         Try to sync the DB to disk (assuming it has a sync, which

Added: gump/trunk/python/gump/stats/mysql/__init__.py
==============================================================================
--- (empty file)
+++ gump/trunk/python/gump/stats/mysql/__init__.py	Wed Aug 11 09:32:36 2004
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+
+# Copyright 2003-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#     http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# tell Python what modules make up the gump.shared package
+__all__ = ["statsdb"]

Added: gump/trunk/python/gump/stats/mysql/statsdb.py
==============================================================================
--- (empty file)
+++ gump/trunk/python/gump/stats/mysql/statsdb.py	Wed Aug 11 09:32:36 2004
@@ -0,0 +1,258 @@
+#!/usr/bin/env python
+# Copyright 2003-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#     http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+
+    MySQL Statistics gathering/manipulation
+    
+"""
+
+import time
+import datetime
+import types
+import os
+import sys
+
+import MySQLdb
+import MySQLdb.cursors
+
+from gump import log
+from gump.core.config import *
+from gump.model.project import Project, ProjectStatistics
+from gump.model.module import Module, ModuleStatistics
+from gump.model.repository import Repository, RepositoryStatistics
+from gump.model.workspace import Workspace, WorkspaceStatistics
+from gump.model.state import *
+
+import gump.utils.mysql
+  
+class StatisticsDB:
+    """
+    	MySQL Statistics Database Interface
+    """
+    
+    FIRST_COLUMN='first'
+    LAST_COLUMN='last'
+    START_OF_STATE_COLUMN='start_of_state'
+    LAST_MODIFIED_COLUMN='last_modified'
+    
+    DATES=[ FIRST_COLUMN, 
+            LAST_COLUMN,
+            START_OF_STATE_COLUMN,
+            LAST_MODIFIED_COLUMN ]
+    
+    ATTR_COLUMN_MAP={
+                        'successes':'successes',
+                        'failures':'failures',
+                        'prereqs':'prereqs',
+                        'first':FIRST_COLUMN,
+                        'last':LAST_COLUMN,
+                        'currentState':'current_state',
+                        'previousState':'previous_state',
+                        'startOfState':START_OF_STATE_COLUMN,
+                        'sequenceInState':'sequence_in_state'
+                    }
+    
+    def __init__(self,dbInfo):
+        self.conn=MySQLdb.Connect(
+                host=dbInfo.getHost(), 
+                user=dbInfo.getUser(),
+                passwd=dbInfo.getPasswd(), 
+                db=dbInfo.getDatabase(),
+                compress=1,
+                cursorclass=MySQLdb.cursors.DictCursor)
+            
+        # print 'ThreadSafe : ' + `MySQLdb.threadsafety`
+        
+        self.helper=gump.utils.mysql.DbHelper(self.conn)
+ 
+    # Workspace
+    def getWorkspaceStats(self,workspaceName):
+        stats=WorkspaceStatistics(workspaceName)
+        try:
+            self._getStats('workspace_stats','workspace_name',workspaceName,stats)
+        except IndexError:
+            pass
+        return stats
+        
+    def putWorkspaceStats(self,stats):
+        self._putStats('workspace_stats','workspace_name',stats)
+
+    def delWorkspaceStats(self,stats):
+        self._delStats('workspace_stats','workspace_name',stats)          
+        
+    # Project    
+    def getProjectStats(self,projectName):
+        stats=ProjectStatistics(projectName)         
+        try:
+            self._getStats('project_stats','project_name',projectName,stats)         
+        except IndexError:
+            pass
+        return stats
+                
+    def putProjectStats(self,stats): 
+        self._putStats('project_stats','project_name',stats)
+        
+    def delProjectStats(self,stats): 
+        self._delStats('project_stats','project_name',stats)
+
+    # Repository 
+    def getRepositoryStats(self,repositoryName):
+        stats=RepositoryStatistics(repositoryName)     
+        try:   
+            self._getStats('repository_stats','repository_name',repositoryName,stats)
+        except IndexError:
+            pass    
+        return stats
+                
+    def putRepositoryStats(self,stats):
+        self._putStats('repository_stats','repository_name',stats)  
+
+    def delRepositoryStats(self,stats):
+        self._delStats('repository_stats','repository_name',stats)          
+
+    # Module    
+    def getModuleStats(self,moduleName):
+        stats=ModuleStatistics(moduleName)        
+        try:
+            settings = self._getStats('module_stats','module_name',moduleName,stats)
+            
+            # Extract that extra
+            if settings.has_key('last_modified') and settings['last_modified']:
+                stats.lastModified=settings['last_modified']
+        except IndexError:
+            pass
+        return stats
+                
+    def putModuleStats(self,stats):
+        extras=None
+        if stats.lastModified:
+            extras={'last_modified':stats.lastModified}        
+        self._putStats('module_stats','module_name',stats,extras)
+
+    def delModuleStats(self,stats):
+        self._delStats('module_stats','module_name',stats)     
+        
+        
+    # Helpers...
+    def _getStats(self,table_name,column_name,entity_name,stats):
+        
+        # Select the row settings from the database
+        settings=self.helper.select(table_name,column_name,entity_name,
+                                StatisticsDB.ATTR_COLUMN_MAP.values())
+        
+        # Extract columns
+        self._getBaseStats(stats,settings)
+            
+        return settings
+        
+    def _putStats(self,table_name,column_name,stats,extras=None): 
+    
+        settings = self._putBaseStats(stats,column_name)
+        if extras: 
+            for (key,value) in extras.items(): settings[key] = value
+            
+        # Perform the update (we've ensured a row exists).
+        self.helper.set(table_name,column_name,stats.name,settings)
+    
+    def _delStats(self,table_name,column_name,stats):       
+        """ Perform an SQL DELETE """        
+        # Perform the delete
+        self.helper.delete(table_name,column_name,stats.name)
+
+    def _getBaseStats(self,stats,settings):
+        """
+        Extract values by name from the DB row
+        """      
+        for (attr, column) in StatisticsDB.ATTR_COLUMN_MAP.items():
+            if settings.has_key(column) and settings[column]:
+                if hasattr(stats,attr):
+                    #print "GET ATTR : " + `type(getattr(stats,attr))`                    
+                    setattr(stats,attr,settings[column])
+        
+    def _putBaseStats(self,stats,column_name): 
+        """
+        Return a dictionary of the named values
+        """
+        settings=dict()
+        
+        settings[column_name] = "'" + stats.name + "'"
+        
+        for (attr, column) in StatisticsDB.ATTR_COLUMN_MAP.items():
+            if hasattr(stats,attr):
+                value = getattr(stats,attr)
+                if value:
+                    if column in StatisticsDB.DATES:   
+                        #print "SET ATTR : " + `value` 
+                        settings[column] = "'" + value.strftime('%Y-%m-%d %H:%M:%S') + "'"
+                    elif isinstance(value,types.StringTypes):
+                        settings[column] = "'" + str(value) + "'"
+                    else:
+                        settings[column] = str(value)
+                
+        return settings
+
+    def sync(self): pass
+    
+if __name__ == '__main__':
+    import logging
+    
+    # init logging
+    logging.basicConfig()
+
+    #set verbosity to show all messages of severity >= default.logLevel
+    log.setLevel(logging.DEBUG)
+    
+    stats=StatisticsDB()
+    
+    # Project
+    ps=stats.getProjectStats('test')
+    ps.successes+=1
+    ps.first=datetime.datetime.now()
+    stats.putProjectStats(ps)     
+    if len(sys.argv) > 1:
+        stats.delProjectStats(ps)
+    print "Project"
+    ps.dump()    
+    
+    sys.exit()
+    
+    # Workspace
+    ws=stats.getWorkspaceStats('test')
+    ws.successes+=1
+    stats.putWorkspaceStats(ws)     
+    if len(sys.argv) > 1:
+        stats.delWorkspaceStats(ws)
+    print "Workspace"
+    ws.dump()    
+    
+    # Module
+    ms=stats.getModuleStats('test')
+    ms.successes+=1
+    stats.putModuleStats(ms)     
+    if len(sys.argv) > 1:
+        stats.delModuleStats(ms)
+    print "Module"
+    ms.dump()
+          
+    # Repository
+    rs=stats.getRepositoryStats('test')
+    rs.successes+=1
+    stats.putRepositoryStats(rs)     
+    if len(sys.argv) > 1:
+        stats.delRepositoryStats(rs)
+    print "Repository"
+    rs.dump()
+          
\ No newline at end of file

Modified: gump/trunk/python/gump/stats/statistician.py
==============================================================================
--- gump/trunk/python/gump/stats/statistician.py	(original)
+++ gump/trunk/python/gump/stats/statistician.py	Wed Aug 11 09:32:36 2004
@@ -25,32 +25,143 @@
 from gump import log
 from gump.run.gumprun import *
 from gump.run.actor import *
-from gump.stats.statsdb import StatisticsDB
 
 class Statistician(AbstractRunActor):
     def __init__(self,run):
         
         AbstractRunActor.__init__(self,run)        
-        self.db=StatisticsDB()   
+        self.db=None
+        
+        # MySQL is optional...
+        if self.run.getWorkspace().hasDatabaseInformation():
+            try:
+                import gump.stats.mysql.statsdb   
+                # Figure out what DB this workspace uses 
+                dbInfo=self.run.getWorkspace().getDatabaseInformation()
+                self.db=gump.stats.mysql.statsdb.StatisticsDB(dbInfo)   
+            except Exception, details:
+                log.error('Failed to load MySQL database driver : %s' % (details), exc_info=1)
+            
+        if not self.db:
+            # DBM is the fallback...
+            import gump.stats.dbm.statsdb            
+            self.db=gump.stats.dbm.statsdb.StatisticsDB()   
         
     def processOtherEvent(self,event):                
+        """
+        Process the 'other' (generic) event, e.g. Init and Finalize
+        In other words, load stats at the start, and update them
+        at the end
+        """
         if isinstance(event,InitializeRunEvent):
             self.loadStatistics()                
         elif isinstance(event,FinalizeRunEvent):          
             if self.run.getOptions().isStatistics():          
                 self.updateStatistics()        
             
-    def loadStatistics(self):
             
-        #
-        # Load stats (and stash onto projects)
-        #    
-        self.db.loadStatistics(self.workspace) 
+    def loadStatistics(self):
+        """
+        
+        Load statistics from the DB onto the objects, so they can
+        reference the latest information (e.g. to set -debug)
         
+        """
+        log.debug('--- Loading Statistics')
+                  
+        # Load the W/S statistics
+        ws=self.db.getWorkspaceStats(self.workspace.getName())
+        self.workspace.setStats(ws)            
+                
+        for repo in self.workspace.getRepositories():
+                        
+            # Load the statistics
+            rs=self.db.getRepositoryStats(repo.getName())
+                
+            # Stash for later...
+            repo.setStats(rs)    
+                      
+        for module in self.workspace.getModules():
+            # Load the statistics...
+            ms=self.db.getModuleStats(module.getName())        
+                
+            # Stash for later...
+            module.setStats(ms)     
+            
+            for project in module.getProjects():
+                # Load the statistics...
+                ps=self.db.getProjectStats(project.getName())        
+                
+                # Stash for later...
+                project.setStats(ps)            
+            
     def updateStatistics(self):
-          
-        #
-        # Update stats (and stash onto projects)
-        #
-        self.db.updateStatistics(self.workspace)            
-        self.db.sync()
\ No newline at end of file
+        """
+        
+        Go through the tree updating statistics as you go...
+        
+        """
+        log.debug('--- Updating Statistics')
+        
+        # Load the W/S statistics
+        ws=self.db.getWorkspaceStats(self.workspace.getName())
+        # Update for this workspace based off this run
+        ws.update(self.workspace)
+        self.workspace.setStats(ws)      
+            
+        # Write out the updates
+        self.db.putWorkspaceStats(ws)        
+                
+        for repo in self.workspace.getRepositories():
+                        
+            # Load the statistics
+            rs=self.db.getRepositoryStats(repo.getName())
+            
+            # Update for this repo based off this run
+            rs.update(repo)
+                
+            # Stash for later...
+            repo.setStats(rs)    
+            
+            # Write out the updates
+            self.db.putRepositoryStats(rs)     
+              
+        for module in self.workspace.getModules():
+                        
+            # Load the statistics
+            ms=self.db.getModuleStats(module.getName())
+            
+            # Update for this project based off this run
+            ms.update(module)
+                
+            # Stash for later...
+            module.setStats(ms)            
+                
+            # Write out the updates
+            self.db.putModuleStats(ms)     
+            
+            for project in module.getProjects():
+                
+                # Load the statistics
+                ps=self.db.getProjectStats(project.getName())
+            
+                # Update for this project based off this run
+                ps.update(project)
+                
+                # Stash for later...
+                project.setStats(ps)            
+                
+                # Write out the updates
+                self.db.putProjectStats(ps) 
+                
+        self.db.sync()
+            
+    def dumpProjects(self):
+        """
+        Show all that is there
+        """
+        for key in self.db.getProjects():
+            print "Project " + pname + " Key " + key
+            s=self.getProjectStats(pname)
+            dump(s)
+                
\ No newline at end of file

Modified: gump/trunk/python/gump/utils/__init__.py
==============================================================================
--- gump/trunk/python/gump/utils/__init__.py	(original)
+++ gump/trunk/python/gump/utils/__init__.py	Wed Aug 11 09:32:36 2004
@@ -27,7 +27,7 @@
 import time
 import urllib
 
-from gump  import log
+from gump import log
 from gump.core.config import default, setting
 
 

Added: gump/trunk/python/gump/utils/mysql.py
==============================================================================
--- (empty file)
+++ gump/trunk/python/gump/utils/mysql.py	Wed Aug 11 09:32:36 2004
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+# Copyright 2003-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#     http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+
+    MySQL Statistics gathering/manipulation
+    
+"""
+
+import MySQLdb
+import MySQLdb.cursors
+
+from gump import log
+  
+class DbHelper:
+    """
+    	MySQL Statistics Database Interface
+    """
+
+    def __init__(self,conn):
+        self.conn=conn
+        
+    def __del__(self):
+        if self.conn:
+            self.conn.close()
+            self.conn=None
+ 
+    def generateSelect(self,table_name,column_name,entity_name):
+        """
+        Generate a select statement, index is a single name
+        """ 
+        statement="SELECT * FROM gump.gump_%s WHERE %s='%s'" % (table_name, column_name, entity_name) 
+        return statement
+        
+    def select(self,table_name,column_name,entity_name,columns):
+        statement=self.generateSelect(table_name,column_name,entity_name)
+        settings = {}
+        cursor = None
+        try:
+            try:
+                cursor = self.conn.cursor()
+                log.debug('SQL: ' + statement)
+                affected=cursor.execute(statement)
+                log.debug('SQL affected: ' + `affected`)
+            
+                row = cursor.fetchall()[0] # Ought be only one...
+          
+                # Extract values
+                for column in columns:
+                    if row.has_key(column) and row[column]:
+                        settings[column]=row[column]
+                        #print 'Extracted %s -> %s' % ( column, row[column])
+                              
+            except Exception, details:
+                if cursor: self.logWarnings(cursor)
+                log.error('SQL Error on [%s] : %s' % (statement, details), exc_info=1)                
+                raise
+        finally:
+            if cursor: cursor.close()         
+
+        return settings
+
+    def set(self,table_name,column_name,entity_name,settings):
+        
+        # Attempt an update (and ensure we affected something)
+        updated=(self.update(table_name,column_name,entity_name,settings) > 0)
+            
+        if not updated:
+            # Attempt an insert
+            self.insert(table_name,settings)
+            
+    def generateInsert(self,table_name,settings): 
+        """ 
+        Perform an SQL INSERT 
+        """
+        statement = "INSERT INTO gump.gump_%s (" % table_name
+        keys=settings.keys()
+        statement += ", ".join(keys)
+        statement += ") VALUES ("
+        statement += ", ".join([str(settings[key]) for key in keys])
+        statement += ")"
+        return statement
+        
+    def insert(self,table_name,settings): 
+        """       
+        Take a dictionary of settings (column names/types) and 
+        perform an insert.        
+        """
+        statement=self.generateInsert(table_name,settings)
+        affected = 0
+        cursor = None
+        try:
+            try:
+                cursor = self.conn.cursor()
+                log.debug('SQL: ' + statement)
+                affected = cursor.execute(statement)   
+                log.debug('SQL Affected: ' + `affected`)                        
+            except Exception, details:
+                if cursor: self.logWarnings(cursor)    
+                log.error('SQL Error on [%s] : %s' % (statement, details), exc_info=1)
+                raise
+        finally:
+            if cursor: cursor.close() 
+        return affected 
+
+    def generateUpdate(self,table_name,column_name,entity_name,settings): 
+        """   
+        Take a dictionary of settings (column names/types) and 
+        generate an update statement. Note: The index is a single name.        
+        """
+        statement = "UPDATE gump.gump_%s SET " % table_name
+        keys=settings.keys()
+        keys.remove(column_name)
+        statement += ", ".join([key + '=' + str(settings[key]) for key in keys])
+        statement += " WHERE %s='%s'" % (column_name, entity_name)
+        return statement
+            
+    def update(self,table_name,column_name,entity_name,settings): 
+        """   
+        Take a dictionary of settings (column names/types) and 
+        perform an update. Note: The index is a single name.        
+        """
+        statement = self.generateUpdate(table_name,column_name,entity_name,settings)
+        affected = 0
+        cursor = None
+        try:
+            try:
+                cursor = self.conn.cursor()
+                log.debug('SQL: ' + statement)
+                affected = cursor.execute(statement)    
+                log.debug('SQL Affected: ' + `affected`)                  
+            except Exception, details:
+                if cursor: self.logWarnings(cursor)    
+                log.error('SQL Error on [%s] : %s' % (statement, details), exc_info=1)
+                raise              
+        finally:
+            if cursor: cursor.close() 
+        return affected 
+
+    def generateDelete(self,table_name,column_name,entity_name):       
+        """ 
+        Perform an SQL DELETE 
+        Index is single name
+        """
+        statement = "DELETE FROM gump.gump_%s WHERE %s='%s'" \
+                        % (table_name, column_name, entity_name)
+        return statement
+
+    def delete(self,table_name,column_name,entity_name):       
+        """ 
+        Perform an SQL DELETE 
+        Index is single name
+        """
+        statement = self.generateDelete(table_name,column_name, entity_name)
+        affected = 0
+        cursor = None
+        try:
+            try:
+                cursor = self.conn.cursor()
+                log.debug('SQL: ' + statement)
+                affected = cursor.execute(statement)       
+                log.debug('SQL Affected: ' + `affected`)              
+            except Exception, details:
+                if cursor: self.logWarnings(cursor)    
+                log.error('SQL Error on [%s] : %s' % (statement, details), exc_info=1)
+                raise                
+        finally:
+            if cursor: cursor.close()     
+        return affected 
+        
+    def logWarnings(self,cursor):
+        if cursor.messages:
+            for (message, details) in cursor.messages:
+                log.warning('SQL Warning:' + str(message) + ':' + str(details))
+            
\ No newline at end of file

Modified: gump/trunk/python/gump/utils/timing.py
==============================================================================
--- gump/trunk/python/gump/utils/timing.py	(original)
+++ gump/trunk/python/gump/utils/timing.py	Wed Aug 11 09:32:36 2004
@@ -76,20 +76,14 @@
     
     return elapsedString    
     
-# Note: Should've defaulted values to -1, but (by accident)
-# set some to 0, which then stuck in the DB. Leave this
-# check in until fixed that (perhaps by looking for 0 in
-# DB).
 def secsToDateTime(secs):
-    if -1 == secs or 0 == secs: return '-'    
-    return time.strftime(setting.datetimeformat, \
-                    time.localtime(secs))    
+    if not secs: return '-'
+    return time.strftime(setting.datetimeformat, time.localtime(secs))    
     
 # See note on secsToDate               
 def secsToTime(secs):
-    if -1 == secs or 0 == secs: return '-'
-    return time.strftime(setting.timeformat, \
-                    time.localtime(secs))                    
+    if not secs: return '-'
+    return time.strftime(setting.timeformat, time.localtime(secs))                    
                 
 def getGeneralSinceDescription(secs, since=None):
     if not since: since = default.time

Modified: gump/trunk/python/gump/utils/work.py
==============================================================================
--- gump/trunk/python/gump/utils/work.py	(original)
+++ gump/trunk/python/gump/utils/work.py	Wed Aug 11 09:32:36 2004
@@ -220,7 +220,6 @@
                 if not startSecs or item.getStartSecs() < startSecs:
                     startSecs=item.getStartSecs()
         if startSecs: return startSecs
-        return -1
     
     def getEndSecs(self):
         endSecs=0
@@ -229,7 +228,6 @@
                 if not endSecs or item.getEndSecs() < endSecs:
                     endSecs=item.getEndSecs()
         if endSecs: return endSecs
-        return -1
     
     def getElapsedSecs(self):
         elapsedSecs=0

Copied: gump/trunk/test/gumptest.sh (from rev 35601, gump/trunk/test/gumpytest.sh)
==============================================================================
--- gump/trunk/test/gumpytest.sh	(original)
+++ gump/trunk/test/gumptest.sh	Wed Aug 11 09:32:36 2004
@@ -36,7 +36,7 @@
 fi
 
 #
-# Perform some Gumpy unit test
+# Perform some Gump unit test
 #
 cd python
 

---------------------------------------------------------------------
To unsubscribe, e-mail: general-unsubscribe@gump.apache.org
For additional commands, e-mail: general-help@gump.apache.org


Re: svn commit: rev 36230 - in gump/trunk: . bin cron log mysql python/gump python/gump/core python/gump/document python/gump/document/xdocs python/gump/java python/gump/model python/gump/mysql python/gump/notify python/gump/runner python/gump/shared pytho

Posted by "Adam R. B. Jack" <aj...@apache.org>.
> ajack@apache.org wrote:
>
> > +def configureDB():
> > +    conn = MySQLdb.Connect(
> > +        host='localhost', user='root',
> > +        passwd='', db='gump',compress=1,
> > +        cursorclass=MySQLdb.cursors.DictCursor)
>
> Why is user, passwd, and db hard coded in mysql/data.py?

That was probably test code, they are no longer hardcoded. They are
defaulted as above, but then overridden using a <database element within the
workspace, with appropriately named attributes.

        <database database="gump_public" passwd="bogus" />

Yup, time to go document. Ok, done. [1]

> A more general question: does BrutusConfig[1] need to be updated to
> reflect any steps related to the setting up of mysql?

It seems somebody (thanks somebody) plus Leo has updated this. We could use
some steps on how the MySQL scripts were run to create the tables. Nick, you
helped me here, you have time to update this?

regards,

Adam

[1] http://gump.apache.org/metadata/workspace.html#database


---------------------------------------------------------------------
To unsubscribe, e-mail: general-unsubscribe@gump.apache.org
For additional commands, e-mail: general-help@gump.apache.org


Re: svn commit: rev 36230 - in gump/trunk: . bin cron log mysql python/gump python/gump/core python/gump/document python/gump/document/xdocs python/gump/java python/gump/model python/gump/mysql python/gump/notify python/gump/runner python/gump/shared python/gump/stats python/gump/stats/dbm python/gump/stats/mysql python/gump/utils test

Posted by Sam Ruby <ru...@apache.org>.
ajack@apache.org wrote:

> +def configureDB():   
> +    conn = MySQLdb.Connect(
> +        host='localhost', user='root',
> +        passwd='', db='gump',compress=1,
> +        cursorclass=MySQLdb.cursors.DictCursor)

Why is user, passwd, and db hard coded in mysql/data.py?

A more general question: does BrutusConfig[1] need to be updated to 
reflect any steps related to the setting up of mysql?

- Sam Ruby

---------------------------------------------------------------------
To unsubscribe, e-mail: general-unsubscribe@gump.apache.org
For additional commands, e-mail: general-help@gump.apache.org