cleared loops.agent package - is now cybertools.agent

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2647 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2008-05-30 05:25:12 +00:00
parent 0a83e7a2d0
commit de60a56f59
86 changed files with 0 additions and 8967 deletions

View file

@ -1,369 +0,0 @@
===============================================================
loops - Linked Objects for Organization and Processing Services
===============================================================
loops agents - running on client systems and other services,
collecting informations and transferring them to the loops server.
($Id$)
This package does not depend on zope or the other loops packages
but represents a standalone application.
But we need a reactor for working with Twisted; in order not to block
testing when running the reactor we use reactor.iterate() calls
wrapped in a ``tester`` object.
>>> from loops.agent.tests import tester
Basic Implementation, Agent Core
================================
The agent uses Twisted's cooperative multitasking model.
This means that all calls to services (like crawler, transporter, ...)
return a deferred that must be supplied with a callback method (and in
most cases also an errback method).
>>> from loops.agent import core
>>> agent = core.Agent()
Configuration Management
========================
Functionality
- Storage of configuration parameters
- Interface to the browser-based user interface that allows the
editing of configuration parameters
All configuration parameters are always accessible via the ``config``
attribute of the agent object.
>>> config = agent.config
This already provides all needed sections (transport, crawl, ui), so
we can directly put information into these sections by loading a
string with the corresponding assignment.
>>> config.load('transport.serverURL = "http://loops.cy55.de"')
>>> config.transport.serverURL
'http://loops.cy55.de'
This setting may also contain indexed access; thus we can model
configuration parameters with multiple instances (like crawling
jobs).
>>> config.load('''
... crawl[0].type = "filesystem"
... crawl[0].directory = "documents/projects"
... ''')
>>> config.crawl[0].type
'filesystem'
>>> config.crawl[0].directory
'documents/projects'
Subsections are created automatically when they are first accessed.
>>> config.load('ui.web.port = 8081')
>>> config.ui.web.port
8081
The ``setdefault()`` method allows to retrieve a value and set
it with a default if not found, in one statement.
>>> config.ui.web.setdefault('port', 8080)
8081
>>> config.transport.setdefault('userName', 'loops')
'loops'
>>> sorted(config.transport.items())
[('__name__', 'transport'), ('serverURL', 'http://loops.cy55.de'), ('userName', 'loops')]
We can output a configuration in a form that is ready for loading
just by converting it to a string representation.
>>> print config
crawl[0].directory = 'documents/projects'
crawl[0].type = 'filesystem'
transport.serverURL = 'http://loops.cy55.de'
transport.userName = 'loops'
ui.web.port = 8081
The configuration may also be saved to a file -
for testing purposes let's use the loops.agent package directory
for storage; normally it would be stored in the users home directory.
>>> import os
>>> os.environ['HOME'] = os.path.dirname(core.__file__)
>>> config.save()
>>> fn = config.getDefaultConfigFile()
>>> fn
'....loops.agent.cfg'
>>> print open(fn).read()
crawl[0].directory = 'documents/projects'
crawl[0].type = 'filesystem'
transport.serverURL = 'http://loops.cy55.de'
transport.userName = 'loops'
ui.web.port = 8081
The simplified syntax
---------------------
>>> config.load('''
... ui(
... web(
... port=11080,
... ))
... crawl[1](
... type='outlook',
... folder='inbox',
... )
... ''')
>>> print config.ui.web.port
11080
Cleaning up
-----------
>>> os.unlink(fn)
Scheduling
==========
Configuration (per job)
- schedule, repeating pattern, conditions
- following job(s), e.g. to start a transfer immediately after a crawl
How does this work?
-------------------
>>> from time import time
>>> from loops.agent.schedule import Job
>>> class TestJob(Job):
... def execute(self):
... d = super(TestJob, self).execute()
... print 'executing'
... return d
>>> scheduler = agent.scheduler
The ``schedule()`` method accepts the start time as a second argument,
if not present use the current time, i.e. start the job immediately.
>>> startTime = scheduler.schedule(TestJob())
>>> tester.iterate()
executing
We can set up a more realistic example using the dummy crawler and transporter
classes from the testing package.
>>> from loops.agent.testing import crawl
>>> from loops.agent.testing import transport
>>> crawlJob = crawl.CrawlingJob()
>>> transporter = transport.Transporter(agent)
>>> transportJob = transporter.createJob()
>>> crawlJob.successors.append(transportJob)
>>> startTime = scheduler.schedule(crawlJob)
The Job class offers two callback hooks: ``whenStarted`` and ``whenFinished``.
Use this for getting notified about the starting and finishing of a job.
>>> def finishedCB(job, result):
... print 'Crawling finished, result:', result
>>> crawlJob.whenFinished = finishedCB
Now let the reactor run...
>>> tester.iterate()
Crawling finished, result: [<loops.agent.testing.crawl.DummyResource ...>]
Transferring: Dummy resource data for testing purposes.
Using configuration with scheduling
-----------------------------------
Let's start with a fresh agent, directly supplying the configuration
(just for testing).
>>> config = '''
... crawl[0].type = 'dummy'
... crawl[0].directory = '~/documents'
... crawl[0].pattern = '*.doc'
... crawl[0].starttime = %s
... crawl[0].transport = 'dummy'
... crawl[0].repeat = 0
... transport.serverURL = 'http://loops.cy55.de'
... ''' % int(time())
>>> agent = core.Agent(config)
We also register our dummy crawling job and transporter classes as
we can not perform real crawling and transfers when testing.
>>> agent.crawlTypes = dict(dummy=crawl.CrawlingJob)
>>> agent.transportTypes = dict(dummy=transport.Transporter)
>>> agent.scheduleJobsFromConfig()
>>> tester.iterate()
Transferring: Dummy resource data for testing purposes.
Crawling
========
General
-------
Functionality
- search for new or changed resources according to the search and
filter criteria
- keep a record of resources transferred already in order to avoid
duplicate transfers (?)
Configuration (per crawl job)
- predefined metadata
Local File System
-----------------
Configuration (per crawl job)
- directories to search
- filter criteria, e.g. file type
Metadata sources
- path, filename
Implementation and documentation: see loops/agent/crawl/filesystem.py
and .../filesystem.txt.
E-Mail-Clients
--------------
Configuration (per crawl job)
- folders to search
- filter criteria (e.g. sender, receiver, subject patterns)
Metadata sources
- folder names (path)
- header fields (sender, receiver, subject, ...)
Special handling
- HTML vs. plain text content: if a mail contains both HTML and plain
text parts the transfer may be limited to one of these parts (configuration
setting)
- attachments may be ignored (configuration setting; useful when attachments
are copied to the local filesystem and transferred from there anyways)
Transport
=========
Configuration
- ``transport.serverURL``: URL of the target loops site, e.g.
"http://z3.loops.cy55.de/bwp/d5"
- ``transport.userName``, ``transport.password`` for logging in to loops
- ``transport.machineName: name under which the client computer is
known to the loops server
- ``transport.method``, e.g. "PUT"
The following information is intended for the default transfer
protocol/method HTTP PUT but probably also pertains to other protocols
like e.g. FTP.
Format/Information structure
----------------------------
- Metadata URL (for storing or accessing metadata sets - optional, see below):
``$loopsSiteURL/resource_meta/$machine_name/$user/$app/$path.xml``
- Resource URL (for storing or accessing the real resources):
``$loopsSiteURL/resource_data/$machine_name//$user/$app/$path``
- ``$app`` names the type of application providing the resource, e.g.
"filesystem" or "mail"
- ``$path`` represents the full path, possibly with drive specification in front
(for filesystem resources on Windows), with special characters URL-escaped
Note that the URL uniquely identifies the resource on the local computer,
so a resource transferred with the exact location (path and filename)
on the local computer as a resource transferred previously will overwrite
the old version, so that the classification of the resource within loops
won't get lost. (This is of no relevance to emails.)
Metadata sets are XML files with metadata for the associated resource.
Usually a metadata set has the extension ".xml"; if the extension is ".zip"
the metadata file is a compressed file that will be expanded on the
server.
Data files may also be compressed in which case there must be a corresponding
entry in the associated metadata set.
Logging
=======
Configuration
- log format(s)
- log file(s) (or other forms of persistence)
Example
-------
We set the logging configuration to log level 20 (INFO) using the
standard log handler that prints to ``sys.stdout``.
>>> agent.config.logging.standard = 20
>>> logger = agent.logger
>>> logger.setup()
The we can log an event providing a dictionary with the data to be logged.
>>> logger.log(dict(object='job', event='start'))
20... event:start object:job
We can also look at the logging records collected in the logger.
>>> len(logger)
1
>>> print logger[-1]
20... event:start object:job
Software Loader
===============
Configuration (general)
- source list: URL(s) of site(s) providing updated or additional packages
Configuration (per install/update job)
- command: install, update, remove
- package names
Browser-based User Interface
============================
The user interface is provided via a browser-based application
based on Twisted and Nevow.

View file

@ -1,4 +0,0 @@
"""
$Id$
"""

View file

@ -1,138 +0,0 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Management of agent configuration.
$Id$
"""
import os
from zope.interface import implements
from loops.agent.interfaces import IConfigurator
_not_found = object()
class Configurator(dict):
implements(IConfigurator)
def __init__(self, *sections, **kw):
for s in sections:
setattr(self, s, ConfigSection(s))
self.filename = kw.get('filename')
def __getitem__(self, key):
return getattr(self, key, ConfigSection(key))
def load(self, p=None, filename=None):
if p is None:
fn = self.getConfigFile(filename)
if fn is not None:
f = open(fn, 'r')
p = f.read()
f.close()
if p is None:
return
exec p in self
def save(self, filename=None):
fn = self.getConfigFile(filename)
if fn is None:
fn = self.getDefaultConfigFile()
if fn is not None:
f = open(fn, 'w')
f.write(repr(self))
f.close()
def __repr__(self):
result = []
for name, value in self.__dict__.items():
if isinstance(value, ConfigSection):
value.collect(name, result)
return '\n'.join(sorted(result))
def getConfigFile(self, filename=None):
if filename is not None:
self.filename = filename
if self.filename is None:
fn = self.getDefaultConfigFile()
if os.path.isfile(fn):
self.filename = fn
return self.filename
def getDefaultConfigFile(self):
return os.path.join(os.path.expanduser('~'), '.loops.agent.cfg')
class ConfigSection(list):
__name__ = '???'
def __init__(self, name=None):
if name is not None:
self.__name__ = name
def __getattr__(self, attr):
value = ConfigSection(attr)
setattr(self, attr, value)
return value
def __getitem__(self, idx):
while idx >= len(self):
self.append(ConfigSection())
return list.__getitem__(self, idx)
def setdefault(self, attr, value):
if attr not in self.__dict__:
setattr(self, attr, value)
return value
return getattr(self, attr)
def items(self):
for name, value in self.__dict__.items():
if isinstance(value, (str, int)):
yield name, value
def __call__(self, *args, **kw):
for s in args:
if isinstance(s, ConfigSection):
# should we update an existing entry?
#old = getattr(self, s.__name__, None)
#if old is not None: # this would have to be done recursively
# old.__dict__.update(s.__dict__)
# for elem in s:
# old.append(elem)
#else:
# or just keep the new one?
setattr(self, s.__name__, s)
for k, v in kw.items():
setattr(self, k, v)
return self
def collect(self, ident, result):
for idx, element in enumerate(self):
element.collect('%s[%i]' % (ident, idx), result)
for name, value in self.__dict__.items():
if isinstance(value, ConfigSection):
value.collect('%s.%s' % (ident, name), result)
elif name != '__name__' and isinstance(value, (str, int)):
result.append('%s.%s = %s' % (ident, name, repr(value)))

View file

@ -1,92 +0,0 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
The real agent stuff.
$Id$
"""
from time import time
import tempfile
from zope.interface import implements
from loops.agent.interfaces import IAgent
from loops.agent.config import Configurator
from loops.agent.crawl import filesystem
from loops.agent.log import Logger
from loops.agent.schedule import Scheduler, Stopper
from loops.agent.transport import base
crawlTypes = dict(
filesystem=filesystem.CrawlingJob,
)
transportTypes = dict(
httpput=base.Transporter,
)
class Agent(object):
implements(IAgent)
crawlTypes = crawlTypes
transportTypes = transportTypes
def __init__(self, conf=None):
config = self.config = Configurator('ui', 'crawl', 'transport', 'logging')
config.load(conf)
self.scheduler = Scheduler(self)
self.stopper = Stopper()
self.stopper.scheduler = self.scheduler
self.logger = Logger(self)
self.tempdir = tempfile.mkdtemp(prefix='loops_')
def scheduleJobsFromConfig(self, stop=False):
config = self.config
scheduler = self.scheduler
lastJob = None
for idx, info in enumerate(config.crawl):
crawlType = info.type
factory = self.crawlTypes.get(crawlType)
if factory is not None:
job = lastJob = factory()
job.params = dict((name, value)
for name, value in info.items()
if name not in job.baseProperties)
transportType = info.transport or 'httpput'
factory = self.transportTypes.get(transportType)
if factory is not None:
params = dict(config.transport.items())
transporter = factory(self, **params)
# TODO: configure transporter or - better -
# set up transporter(s) just once
job.successors.append(transporter.createJob())
job.repeat = info.repeat or 0
self.scheduler.schedule(job, info.starttime or int(time()))
# TODO: remove job from config
# TODO: put repeating info in config
# TODO: remember last run for repeating job
if stop:
if lastJob is not None:
lastTrJob = lastJob.successors[-1]
lastTrJob.successors.append(self.stopper)
else:
self.scheduler.schedule(self.stopper)

View file

@ -1,4 +0,0 @@
"""
$Id$
"""

View file

@ -1,59 +0,0 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Filesystem crawler.
$Id$
"""
from zope.interface import implements
from loops.agent.interfaces import ICrawlingJob, IMetadataSet
from loops.agent.schedule import Job
class CrawlingJob(Job):
implements(ICrawlingJob)
baseProperties = ('starttime', 'type', 'repeat', 'transportType',)
def __init__(self, **params):
self.predefinedMetadata = {}
super(CrawlingJob, self).__init__(**params)
def execute(self):
return self.collect()
class Metadata(dict):
implements(IMetadataSet)
def __init__(self, data=dict()):
for k in data:
self[k] = data[k]
def asXML(self):
# TODO...
return ''
def set(self, key, value):
self['key'] = value

View file

@ -1,84 +0,0 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Filesystem crawler.
$Id$
"""
import os
from fnmatch import filter
from datetime import datetime
from twisted.internet.defer import Deferred
from twisted.internet.task import coiterate
from zope.interface import implements
from loops.agent.interfaces import IResource
from loops.agent.crawl.base import CrawlingJob as BaseCrawlingJob
from loops.agent.crawl.base import Metadata
class CrawlingJob(BaseCrawlingJob):
def collect(self):
self.collected = []
coiterate(self.crawlFilesystem()).addCallback(self.finished)
# TODO: addErrback()
self.deferred = Deferred()
return self.deferred
def finished(self, result):
self.deferred.callback(self.collected)
def crawlFilesystem(self):
directory = self.params.get('directory')
pattern = self.params.get('pattern') or '*'
lastRun = self.params.get('lastrun') or datetime(1980, 1, 1)
for path, dirs, files in os.walk(directory):
if '.svn' in dirs:
del dirs[dirs.index('.svn')]
self.loadFiles(files, pattern)
def loadFiles(self, files, pattern):
for f in filter(files, pattern):
filename = os.path.join(path, f)
mtime = datetime.fromtimestamp(os.path.getmtime(filename))
if mtime <= lastRun: # file not changed
continue
meta = dict(
path=filename,
)
self.collected.append(FileResource(filename, Metadata(meta)))
yield None
class FileResource(object):
implements(IResource)
def __init__(self, path, metadata=None):
self.path = path
self.metadata = metadata
application = 'filesystem'
@property
def data(self):
return open(self.path, 'r')

View file

@ -1,42 +0,0 @@
=====================================================
loops.agent.crawl.filesystem - The Filesystem Crawler
=====================================================
($Id$)
>>> import os
>>> from time import time
>>> from loops.agent.tests import tester, baseDir
>>> from loops.agent.core import Agent
>>> from loops.agent.crawl.filesystem import CrawlingJob
>>> agent = Agent()
>>> startTime = scheduler = agent.scheduler
We create a crawling job that should scan the data subdirectory
of the testing directory in the loops.agent package.
>>> dirname = os.path.join(baseDir, 'testing', 'data')
>>> crawlJob = CrawlingJob(directory=dirname)
The result of the crawling process should be transferred using
the dummy transporter from the testing package; this just prints
an informative message with the contents of the files to be
transferred.
>>> from loops.agent.testing import transport
>>> transporter = transport.Transporter(agent)
>>> transportJob = transporter.createJob()
>>> crawlJob.successors.append(transportJob)
We are now ready to schedule the job and let the reactor execute it.
>>> startTime = scheduler.schedule(crawlJob)
>>> tester.iterate()
Metadata: {'path': '...data...subdir...file2.txt'}
Transferring: Data from file2.txt
Metadata: {'path': '...data...file1.txt'}
Transferring: Data from file1.txt

View file

@ -1,277 +0,0 @@
"""
This module reads out information from Microsoft Outlook.
The function loadInbox() reads all Emails of MsOutlook-inbox folder,
optionally it is possible to read the subfolder of the inbox too.
The emails will be returnes as Python MIME objects in a list.
Tobias Schmid 26.07.2007
"""
import win32com.client
import ctypes
import win32api, win32process, win32con
import re
from email.mime.multipart import MIMEMultipart
from watsup.winGuiAuto import findTopWindow, findControl, findControls, clickButton, \
getComboboxItems, selectComboboxItem, setCheckBox
from twisted.internet.defer import Deferred
from twisted.internet.task import coiterate
from twisted.internet import threads
from zope.interface import implements
from loops.agent.interfaces import IResource
from loops.agent.crawl.base import CrawlingJob as BaseCrawlingJob
from loops.agent.crawl.base import Metadata
# DEBUG FLAGS
DEBUG = 1
DEBUG_WRITELINE = 1
# some constants
COMMASPACE = ', '
class CrawlingJob(BaseCrawlingJob):
keys = ""
inbox = ""
subfolders = ""
pattern = ""
def collect(self):
self.collected = []
coiterate(self.crawlOutlook()).addCallback(self.finished)
# TODO: addErrback()
self.deferred = Deferred()
return self.deferred
def finished(self, result):
self.deferred.callback(self.collected)
def crawlOutlook(self):
outlookFound = 0
try:
oOutlookApp = \
win32com.client.gencache.EnsureDispatch("Outlook.Application")
outlookFound = 1
except:
print "MSOutlook: unable to load Outlook"
records = []
if not outlookFound:
return
# fetch the params
criteria = self.params
self.keys = criteria.get('keys')
self.inbox = criteria.get('inbox') #boolean
self.subfolders = criteria.get('subfolders') #boolean
self.pattern = criteria.get('pattern')
if self.pattern != '':
self.pattern = re.compile(criteria.get('pattern') or '.*')
else:
self.pattern = None
if DEBUG_WRITELINE:
print 'MSOutlook.loadInbox() ===> starting'
# try to handle the Outlook dialog
#d = threads.deferToThread(self.handleOutlookDialog)
#d.addCallback(self.printResult)
# catch Inbox folder
onMAPI = oOutlookApp.GetNamespace("MAPI")
ofInbox = \
onMAPI.GetDefaultFolder(win32com.client.constants.olFolderInbox)
# fetch the mails of the inbox folder
if DEBUG_WRITELINE:
print 'MSOutlook.loadInbox() ===> fetch mails of inbox folder'
# fetch mails from inbox
if self.inbox:
self.loadEmail(ofInbox)
# fetch mails of inbox subfolders
if self.subfolders and self.pattern is None:
if DEBUG_WRITELINE:
print 'MSOutlook.loadInbox() ===> fetch emails of subfolders'
lInboxSubfolders = getattr(ofInbox, 'Folders')
for of in range(lInboxSubfolders.__len__()):
# get a MAPI-folder object and load its emails
self.loadEmail(lInboxSubfolders.Item(of + 1))
# pattern, just read the specified subfolder
elif self.subfolders and self.pattern:
if DEBUG_WRITELINE:
print 'MSOutlook.loadInbox() ===> fetch emails of specified subfolder'
lInboxSubfolders = getattr(ofInbox, 'Folders')
for of in range(lInboxSubfolders.__len__()):
# get a MAPI-folder object and load its emails
if self.pattern.match(getattr(lInboxSubfolders.Item(of + 1), 'Name')):
self.loadEmail(lInboxSubfolders.Item(of + 1)) #oFolder
if DEBUG:
print 'number of mails in Inbox:', len(ofInbox.Items)
# list of _Folder (subfolder of inbox)
lInboxSubfolders = getattr(ofInbox, 'Folders')
# get Count-Attribute of _Folders class
iInboxSubfoldersCount = getattr(lInboxSubfolders, 'Count')
# the Item-Method of the _Folders class returns a MAPIFolder object
oFolder = lInboxSubfolders.Item(1)
print 'Count of Inbox-SubFolders:', iInboxSubfoldersCount
print 'Inbox sub folders (Folder/Mails):'
for folder in range(iInboxSubfoldersCount):
oFolder = lInboxSubfolders.Item(folder+1)
print getattr(oFolder, 'Name'), '/' , len(getattr(oFolder, 'Items'))
if DEBUG_WRITELINE:
print 'MSOutlook.loadInbox() ===> ending'
yield '1'
def loadEmail(self, oFolder):
# get items of the folder
folderItems = getattr(oFolder, 'Items')
for item in range(len(folderItems)):
mail = folderItems.Item(item+1)
if mail.Class == win32com.client.constants.olMail:
if self.keys is None:
self.keys = []
for key in mail._prop_map_get_:
if isinstance(getattr(mail, key), (int, str, unicode)):
self.keys.append(key)
if DEBUG:
self.keys.sort()
print 'Fiels\n======================================='
for key in self.keys:
print key
record = {}
for key in self.keys:
record[key] = getattr(mail, key)
if DEBUG:
print str(item)
# Create the mime email object
msg = self.createEmailMime(record)
# list with mime objects
self.collected.append((OutlookResource(msg)))
def createEmailMime(self, emails):
# Create the container (outer) email message.
msg = MIMEMultipart()
# subject
msg['Subject'] = emails['Subject'].encode('utf-8')
# sender
if emails.has_key('SenderEmailAddress'):
sender = str(emails['SenderEmailAddress'].encode('utf-8'))
else:
sender = str(emails['SenderName'].encode('utf-8'))
msg['From'] = sender
# CC
#msg['CC'] = str(emails['CC'].encode('utf-8'))
# ReceivedTime
#msg['Date'] = str(emails['ReceivedTime'])
#recipients
recipients = []
if emails.has_key('Recipients'):
for rec in range(emails['Recipients'].__len__()):
recipients.append(getattr(emails['Recipients'].Item(rec+1), 'Address'))
msg['To'] = COMMASPACE.join(recipients)
else:
recipients.append(emails['To'])
msg['To'] = COMMASPACE.join(recipients)
# message
msg.preamble = emails['Body'].encode('utf-8')
return msg
def handleOutlookDialog(self):
"""
This function handles the outlook dialog, which appears if someone
tries to access to MS Outlook.
"""
hwnd = None
while True:
hwnd = ctypes.windll.user32.FindWindowExA(None, hwnd, None, None)
print 'searching....'
if hwnd == None:
break
else:
val = u"\0" * 1024
ctypes.windll.user32.GetWindowTextW(hwnd, val, len(val))
val = val.replace(u"\000", u"")
if val and repr(val) == "u'Microsoft Office Outlook'":
print repr(val)
print '===> MSOutlook dialog box found'
#tid, pid = win32process.GetWindowThreadProcessId(hwnd)
# get a handle
#handle = win32api.OpenProcess(win32con.PROCESS_TERMINATE, 0, pid)
# terminate the process by the handle
#win32api.TerminateProcess(handle, 0)
# close the handle - thankyou
#win32api.CloseHandle(handle)
# get the Main Control
form = findTopWindow(wantedText='Microsoft Office Outlook')
controls = findControls(form)
# get the check box
checkBox = findControl(form, wantedText='Zugriff')
setCheckBox(checkBox, 1)
# get the combo box
comboBox = findControl(form, wantedClass='ComboBox')
items = getComboboxItems(comboBox)
selectComboboxItem(comboBox, items[3])#'10 Minuten'
# finally get the button and click it
button = findControl(form, wantedText = 'Erteilen')
clickButton(button)
print '-> dialog found and handled'
break
def printResult(self):
print '--> Outlook dialog handled'
class OutlookResource(object):
implements(IResource)
def __init__(self, oEmail):
self.oEmail = oEmail
@property
def data(self):
return self.oEmail

View file

@ -1,152 +0,0 @@
""" Class and functions to find the windowText and className for a given executable
"""
# Author : Tim Couper - tim@2wave.net
# Date : 22 July 2004
# Version : 1.1
# Copyright : Copyright TAC Software Ltd, under Python-style licence.
# Provided as-is, with no warranty.
# Notes : Requires Python 2.3, win32all, ctypes & watsup package
import os.path
from watsup.winGuiAuto import findTopWindows,findControls,dumpWindow,\
dumpTopWindows
from watsup.utils import dumpHwnd,tupleHwnd
from watsup.launcher import AppThread,terminateApp
from watsup.OrderedDict import OrderedDict
from time import time,sleep
from types import ListType
THREAD_TIMEOUT_SECS=2.0
INDENT=2
#wstruct is of the form [hwnd,wclass,wtext] or
# [hwnd,wclass,wtext,[winstruct]
##def printwstruct(wstruct):
## print 'printFormat had trouble with:\n%s' % str(wstruct[3])
## import pprint
## print '------'
## pprint.pprint(wstruct)
## print '------'
def printFormat(wstruct,indent=0):
def printwstruct(wstruct):
print 'printFormat had trouble with:\n%s' % str(wstruct[3])
import pprint
print '------'
pprint.pprint(wstruct)
print '------'
"""wstruct is either a wintuple, or a recursive list of wintuples
"""
# type 1
if type(wstruct[1]) is not ListType:
print '%s%s: %s' % (' '*indent,wstruct[2],wstruct[1])
if len(wstruct)>3 and wstruct[3]<>None:
try:
for item in wstruct[3]:
printFormat(item,indent+INDENT)
except:
print_wstruct(wstruct)
# type 2:
else:
for item in wstruct[1]:
printFormat(item,indent+INDENT)
class AppControls(object):
def __init__(self,program,wantedText=None,wantedClass=None,selectionFunction=None,verbose=False):
self.wantedText=wantedText
self.wantedClass=wantedClass
self.selectionFunction=selectionFunction
self.verbose=verbose
topHwnds,unwantedHwnds,self.appThread=findAppTopWindows(program,verbose=verbose) #should only be one, but you never know
self.topHwnds=[]
self.unwantedHwnds=unwantedHwnds
def run(self):
while self.appThread.isAlive():
results=findNewTopWindows(self.unwantedHwnds,self.verbose)
if results:
self.process(results) # update the list of topWindows and unwanted TopWindows
def process(self,results):
for hwnd in results:
ctHwnds=findControls(hwnd)
if ctHwnds:
# we only add hwnd if there are controlHwnds
# as there may be a form which exists
# as an hwnd, but has not controls materialised yet
self.unwantedHwnds.append(hwnd)
self.write(hwnd)
for ctHwnd in ctHwnds:
self.unwantedHwnds.append(ctHwnd)
def write(self,hwnd):
h=tupleHwnd(hwnd)
t=[h[0],h[1],h[2],dumpWindow(hwnd)]
printFormat(t)
def findNewTopWindows(unwantedHwnds=[],verbose=False):
# returns a list of all top windows' hwnds we haven't
# found yet
htuples=dumpTopWindows()
if verbose:
print '..%d windows found (%d windows in unwanted)' % (len(htuples),len(unwantedHwnds))
results=[]
for htuple in htuples:
hwnd=htuple[0]
if hwnd not in unwantedHwnds:
if verbose:
print '..adding %s' % dumpHwnd(hwnd)
results.append(hwnd)
return results
def findAppTopWindows(program,verbose=False):
"""returns the hwnds for the program, along with the hwnds for the
stuff that are to be ignored
"""
# first we run findTopWindows before launching the program; store the hwnds found
# (note that it doesn't matter if an instance of the program IS running
# as we'll start another one whose hwnds will be new)
# run findTopWindows, and remove from the list any which are stored
unwantedHwnds=findNewTopWindows() # list of topWindow hwnds that exist
if verbose:
print '..%d original window(s)' % len(unwantedHwnds)
# run the program
appThread=AppThread(program,verbose)
appThread.start()
# give the thread a chance to get going:
results=[]
t=time()
while not results and ((time()-t)<THREAD_TIMEOUT_SECS):
sleep(0.2) # means that the thread hasn't launched; give it a chance
results=findNewTopWindows(unwantedHwnds,verbose)
if not results:
# stop the program
terminateApp()
raise Exception, 'Failed to find any new windows!'
if verbose:
print '..%d additional (new) non-trivial window(s)' % len(results)
if verbose:
for hwnd in results:
print '..%s: %s' % tupleHwnd(hwnd)[:2]
return results,unwantedHwnds,appThread
if __name__=='__main__':
pass

View file

@ -1,61 +0,0 @@
class OrderedDict(dict):
def __init__(self,dict=None):
""" currently, dict parameter does not work """
self._keys = []
dict.__init__(self,dict)
def __delitem__(self, key):
dict.__delitem__(self, key)
self._keys.remove(key)
def __setitem__(self, key, item):
dict.__setitem__(self, key, item)
if key not in self._keys: self._keys.append(key)
def clear(self):
dict.clear(self)
self._keys = []
def copy(self):
dict = dict.copy(self)
dict._keys = self._keys[:]
return dict
def items(self):
return zip(self._keys, self.values())
def keys(self):
return self._keys
def popitem(self):
try:
key = self._keys[-1]
except IndexError:
raise KeyError('dictionary is empty')
val = self[key]
del self[key]
return (key, val)
def setdefault(self, key, failobj = None):
dict.setdefault(self, key, failobj)
if key not in self._keys: self._keys.append(key)
def update(self, dict):
dict.update(self, dict)
for key in dict.keys():
if key not in self._keys: self._keys.append(key)
def values(self):
return map(self.get, self._keys)
def __str(self):
return self.__repr__()
def __repr__(self):
itemList=[]
for item in self.items():
itemList.append('%s: %s' % item)
return '{'+', '.join(itemList)+'}'

View file

@ -1,389 +0,0 @@
from watsup.winGuiAuto import findControl,findControls,findTopWindow, \
WinGuiAutoError,getEditText,clickButton, \
activateMenuItem,getMenuInfo, \
getListboxItems,getComboboxItems, \
selectListboxItem,selectComboboxItem,\
setEditText,getTopMenu, setCheckBox,\
getCheckBox
from watsup.launcher import launchApp,terminateApp
import win32gui
import win32con
#from watsup.Knowledge import getKnowledge
from types import ListType,TupleType
verbose=False
CONTROL_MAX_WAIT_SECS=3
def PLaunchApp(program,wantedText=None,wantedClass=None):
hwnd=launchApp(program,wantedText,wantedClass,verbose)
return PWindow(hwnd=hwnd)
class PWinControl(object):
"""Abstract base class for PWindows and PControls"""
def __init__(self,parent):
self.parent=parent
def findPControl(self,hwnd=None,wantedText=None,wantedClass=None,selectionFunction=None):
"""Factory method returning a PControl instance, or a subclass thereof,
within a PWinControl instance,
find a unique control - raises exception if non-unique
"""
# if wantedClass is not given, let's try and find out what it is;
# then we should be better able to assign the right PControl subclass
# to it
if wantedClass==None:
#find the wantedClass anyway
p=PControl(self.parent,hwnd,wantedText,wantedClass,selectionFunction)
wantedClass=p.className
else:
p=None
# if this is a known class, return the instance of the specific
# subclass of PControl
if KNOWN_CLASSES.has_key(wantedClass):
if verbose:
print KNOWN_CLASSES[wantedClass],(self.parent,hwnd,wantedText,wantedClass,selectionFunction)
return KNOWN_CLASSES[wantedClass](self.parent,hwnd,wantedText,wantedClass,selectionFunction)
# in all other cases, return a PControl (we may have already calculated it above)
if p:
return p
else:
return PControl(self.parent,hwnd,wantedText,wantedClass,selectionFunction)
def findPControls(self,wantedText=None,wantedClass=None,selectionFunction=None):
# returns a list of PControl instances which match the criteria
hwnds=findControls(wantedText,wantedClass,selectionFunction)
controlList=[]
for hwnd in hwnds:
controlList.append(self.findPControl(self,hwnd=hwnd))
return controlList
##class Menu(object):
## # container for menu items
## def __init__(self,mwnd):
## self.mwnd=mwnd
# not sure we need this entity, as we can address MenuItems directly
class MenuItem(object):
def __init__(self,parentWindow,*menuItems):
self.parentWindow=parentWindow
#acceept either a tuple/list or *args-type values
if type(menuItems[0]) in (ListType,TupleType):
self.menuItemTuple=menuItems[0]
else:
self.menuItemTuple=menuItems
def activate(self):
activateMenuItem(self.parentWindow.hwnd,self.menuItemTuple)
#-------------------------------------------------------------------------------
# accessors and properties
def getInfo(self):
return getMenuInfo(self.parentWindow.hwnd,self.menuItemTuple)
def getName(self):
return menuInfo.name
def getItemCount(self):
return menuInfo.itemCount
def getIsChecked(self):
return menuInfo.IsChecked
def getIsSeparator(self):
return menuInfo.IsSeparator
def getIsDisabled(self):
return menuInfo.IsDisabled
def getIsGreyed(self):
return menuInfo.IsGreyed
name=property(getName)
itemCount=property(getItemCount)
isChecked=property(getIsChecked)
isDisabled = property(getIsDisabled)
isGreyed = property(getIsGreyed)
isSeparator = property(getIsSeparator)
menuInfo=property(getInfo)
#-------------------------------------------------------------------------------
class PWindowError(Exception): pass
class PWindow(PWinControl):
def __init__(self,hwnd=None,wantedText=None,wantedClass=None,selectionFunction=None,controlParameters=None):
PWinControl.__init__(self,self)
if hwnd:
self.hwnd=hwnd
else:
try:
self.hwnd=findTopWindow(wantedText=wantedText,
wantedClass=wantedClass,
selectionFunction=selectionFunction)
except WinGuiAutoError,e:
raise PWindowError,e
# controlParameters is the list of dictionaries with unique
# definitions of the controls within this Window
# eg controlParameters=[{'wantedClass':'TButton','wantedText':'Button1'},
# {'wantedClass':'TRadioButton','selectionFunction':chooseIt}]
self.controls=[]
if controlParameters<>None:
for cp in controlParameters:
hwnd=cp.get('hwnd',None)
wantedClass=cp.get('wantedClass',None)
wantedText=cp.get('wantedTest',None)
selectionFunction=cp.get('selectionFunction',None)
clist=self.findControls(hwnd=hwnd,
wantedText=wantedText,
wantedClass=wantedClass,
selectionFunction=selectionFunction)
self.controls.extend(clist)
self._mainMenuHandle=None
def activateMenuItem(self,menuItem):
menuItem.activateMenuItem()
def terminate(self):
terminateApp(self.hwnd)
#-------------------------------------------------------------------------------
#Accessors & properties
### top menu item
## def getMainMenu(self):
## if self._mainMenuHandle:
## return self._mainMenuHandle
## else:
## return getTopMenu(self.hwnd)
##
## mainMenu=property(getMainMenu)
#-------------------------------------------------------------------------------
class PControlError(Exception): pass
class PControl(PWinControl):
def __init__(self,parent,hwnd=None,wantedText=None,wantedClass=None,selectionFunction=None):
"Constructor takes either hwnd directly, or others in a controlParameter set"
PWinControl.__init__(self,parent)
if hwnd:
self.hwnd=hwnd
else:
try:
self.hwnd=findControl(parent.hwnd,
wantedText=wantedText,
wantedClass=wantedClass,
selectionFunction=selectionFunction,
maxWait=CONTROL_MAX_WAIT_SECS)
except WinGuiAutoError,e:
raise PControlError,e
## def addKnowledge(self,attrName):
## knowledge=getKnowledge()
## knowledge.add(self.className,attrName)
#-------------------------------------------------------------------------------
# general winctrl actions which, if acted upon by a user, might tell us sth about
# this unknown control
def getItems(self):
# This PControl of unknown class can get items
# at least the user thinks so
#self.addKnowledge('getItems')
res=getComboboxItems(self.hwnd)
if not res:
res=getListboxItems(self.hwnd)
return res
def selectItem(self,item):
# This PControl of unknown class can select items
# at least the user thinks so
#self.addKnowledge('selectItem')
res= selectListboxItem(self.hwnd, item)
if not res:
res=selectComboBoxItem(self.hwnd,item)
return res
def click(self):
# This PControl of unknown class is clickable
# at least the user thinks so
#self.addKnowledge('click')
clickButton(self.hwnd)
def getCaption(self):
# This PControl of unknown class has a caption,
# at least the user thinks so
#self.addKnowledge('caption')
return self.getEditText()
def setCheckBox(self, state=3DTrue):
setCheckBox(self.hwnd, state)
def getCheckBox(self):
return getCheckBox(self.hwnd)
#-------------------------------------------------------------------------------
#Accessors and properties
def getText(self):
# returns a list of strings which make up the text
return getEditText(self.hwnd)
def setText(self,text,append=False):
setEditText(text,append)
def getClassName(self):
return win32gui.GetClassName(self.hwnd)
text=property(getText)
className=property(getClassName)
caption=property(getCaption)
items=property(getItems)
#-------------------------------------------------------------------------------
class PEdit(PControl):
def __init__(self,parent,hwnd=None,wantedText=None,wantedClass=None,selectionFunction=None):
PControl.__init__(self,parent,hwnd,wantedText,wantedClass,selectionFunction)
#-------------------------------------------------------------------------------
#Accessors and properties
def getText(self):
# returns a simple string - PEdit controls only have one value
p=PControl.getText(self)
if p:
return p[0]
else:
return ''
text=property(getText)
caption=None #undefine the caption property
#-------------------------------------------------------------------------------
class PText(PControl):
# multi-line text control
caption=None
class PComboBox(PControl):
def selectItem(self,item):
selectComboboxItem(self.hwnd,item)
#-------------------------------------------------------------------------------
#Accessors and properties
def getItems(self):
return getComboboxItems(self.hwnd)
items=property(getItems)
#-------------------------------------------------------------------------------
class PDelphiComboBox(PComboBox):
# The Delphi Combo box has a control of class Edit within
# it, which contains the text
def __init__(self,parent,hwnd=None,wantedText=None,wantedClass=None,selectionFunction=None):
PControl.__init__(self,parent,hwnd,wantedText,wantedClass,selectionFunction)
self.editCtrl=self.findPControl(wantedClass='Edit')
#-------------------------------------------------------------------------------
#Accessors and properties
def getText(self):
# get the content from the control Edit:
return self.editCtrl.getText()
text=property(getText)
#-------------------------------------------------------------------------------
class PButton(PControl):
def click(self):
clickButton(self.hwnd)
#-------------------------------------------------------------------------------
#Accessors and properties
caption=property(PControl.getText)
#-------------------------------------------------------------------------------
class PListBox(PControl):
def selectItem(self,item):
return selectListboxItem(self.hwnd, item)
#-------------------------------------------------------------------------------
#Accessors and properties
def getItems(self):
return getListboxItems(self.hwnd)
items=property(getItems)
#-------------------------------------------------------------------------------
class PCheckBox(PControl):
#-------------------------------------------------------------------------------
#Accessors and properties
caption=property(PControl.getText)
def getCheckStatus(self):
return self.getCheckBox()
def isChecked(self):
return self.getCheckStatus()#=3D=3D win32con.BST_INDETERMINATE
def isIndeterminate(self):
return self.getCheckStatus() #=3D=3D win32con.BST_INDETERMINATE
=20
def isNotChecked(self):
return self.getCheckStatus() #=3D=3D win32con.BST_UNCHECKED
=20
def setChecked(self):
setCheckBox(hwnd, state = True)
def setUnChecked(self):
setCheckBox(hwnd, state = False)
#-------------------------------------------------------------------------------
KNOWN_CLASSES={'TEdit': PEdit,
'TComboBox': PDelphiComboBox,
'ComboBox': PComboBox,
'TButton': PButton,
'TListBox': PListBox,
'ListBox': PListBox,
'CheckBox': PCheckBox,
'TCheckBox': PCheckBox,
}

View file

@ -1,10 +0,0 @@
from watsup.winGuiAuto import findTopWindow,findControl,setEditText
from time import sleep
# Locate notepad's edit area, and enter various bits of text.
notepadWindow = findTopWindow(wantedClass='Notepad')
editArea = findControl(notepadWindow,wantedClass="Edit")
setEditText(editArea, "Hello, again!")
sleep(0.8)
setEditText(editArea, " You still there?",True)

View file

@ -1,27 +0,0 @@
from watsup.winGuiAuto import findControl,setEditText, findTopWindow,clickButton
import os
import os.path
FILENAME='atestfile.txt'
def main():
# delete any occurrence of this file from the disk
if os.path.exists(FILENAME):
os.remove(FILENAME)
form=findTopWindow(wantedText='Simple Form')
button=findControl(form,wantedText='Create file')
editbox=findControl(form,wantedClass='TEdit')
# enter a filename:
setEditText(editbox,[FILENAME])
clickButton(button)
# now check that the file is there
if os.path.exists(FILENAME):
print 'file %s is present' %FILENAME
else:
print "file %s isn't there" % FILENAME
if __name__=='__main__':
main()

View file

@ -1,17 +0,0 @@
from watsup.launcher import launchApp,terminateApp
from watsup.winGuiAuto import findTopWindows
import example2
# find an instance of SimpleForm. If one isn't there, launch it
forms=findTopWindows(wantedText='Simple Form')
if forms:
form=forms[0]
else:
form=launchApp('simple.exe',wantedText='Simple Form')
example2.main()
# and terminate the form
terminateApp(form)

View file

@ -1,51 +0,0 @@
from watsup.launcher import launchApp,terminateApp
from watsup.winGuiAuto import findTopWindows, findControl,getEditText,clickButton
from watsup.performance import PerformanceCheck,PerformanceCheckError
from time import sleep,time
def main(myExecutable,myWantedText):
# find an instance of SimpleForm. If one isn't there, launch it
forms=findTopWindows(wantedText=myWantedText)
if forms:
form=forms[0]
else:
form=launchApp(myExecutable,wantedText=myWantedText)
button=findControl(form,wantedText='Click me')
editbox=findControl(form,wantedClass='TEdit')
#start a performance check instance
p=PerformanceCheck()
clickButton(button)
# belts and braces to avoid infinite waiting!
maxWaitTime=2.0
startTime=time()
while time()-startTime<maxWaitTime:
t=getEditText(editbox)
if t:
break
else:
sleep(0.1)
else:
raise Exception,'Failed to get value after maxWaitTime of %s secs' % maxWaitTime
try:
try:
#do the check/recording step, identifying this step with the wantedtext
p.check(myWantedText,1.0)
except PerformanceCheckError,e:
print '** Failed: %s' % e
# and terminate the form
finally:
terminateApp(form)
from watsup.performance import nicePrint
nicePrint()
if __name__=='__main__':
print ' please run example4a or 4b'

View file

@ -1,3 +0,0 @@
from example4 import main
main('perform/perform.exe','Performance Form 1')

View file

@ -1,3 +0,0 @@
from example4 import main
main('perform/perform2.exe','Performance Form 2')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

View file

@ -1,343 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Windows Application Test System Using Python</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link href="../tac.css" rel="stylesheet" type="text/css">
</head>
<body>
<img src="../images/tizmoi.jpg">
<H2>WATSUP - Windows Application Test System Using Python</H2>
The WATSUP toolkit is designed to allow the automated test of Windows applications.
The system uses the "object-based" mechanism for identifying and invoking actions
on controls and menu items.
<P>So much has been written about the scope, robustness, scalability and outstanding
usability of Python that I'll go no further with it here, only to say that if
you haven't yet had a chance to use this comprehensive, open source language,
don't miss out on the opportunity to take a serious look at it!</P>
<p>The examples in this document assume a basic familiarity with Python.</p>
<H2>Functional Tests</H2>
Testers/developers write automated functional tests which follow a prescriptive,
possibly branching, possibly dynamic "user workflow". The script can check for
changes in the gui itself, operating system environment, file system, database
table and records, network, internet or extranet urls/pages/web services ... - in
fact anywhere that there could be changes.
<p>
Examination of the functions in module autoWinGui.py within the watsup package shows the variety of windows control items
that can be checked/modified. These include:
<ul>
<li>Get and set text in editable controls</li>
<li>Edit and select items from controls supporting lists</li>
<li>Click and double-click controls to invoke their actions</li>
<li>Determine the state of menu items and invoke them</li>
</ul>
<p> The system also provides tools for finding windows by caption and/or class,
controls by text/caption and/or class, and menu items by text or position. (One
of the aspirations of this project is to continue to extend the list to include
as many controls as possible) .</p>
<H3>Example 1 - automated writing on Notepad</H3>
<p>Here's a simple example of the control over applications that you can have with watsup.
First, launch notepad from:</p> <p>Windows Start Menu - All Programs - Accessories - Notepad</p>
Then run the following script (<a href="code/example1.py">Example 1</a>)
<code>
<pre>
from watsup.winGuiAuto import findTopWindow,findControl,setEditText
from time import sleep
# Locate notepad's edit area, and enter various bits of text.
notepadWindow = findTopWindow(wantedClass='Notepad')
editArea = findControl(notepadWindow,wantedClass="Edit")
setEditText(editArea, "Hello, again!")
sleep(0.8)
setEditText(editArea, " You still there?",True)
</pre></code>
Finally, close notepad.<p></p>
<h3>Example 2 - testing a simple example </h3>
In functional tests, the tester wants to ensure that
the cause - invoking a sequence of windows events (button clicks, menu item activation)
has the predicted effect of, for example, a change in a value of a sindows control,
the creation of a file, or the entry of a new database record.
See the directory watsup/examples/simple directory for the executable simple.exe.
If you run the application, you see a button and a text box.
Enter a valid filename into the box, say xyz, and
click the button;
after the file is created, a message box appears containing a success message,
and investigation of the directory watsup/examples/simple will show a file
called 'xyz.txt' has been created (or overwritten).
<p>Now let's script a test to automate this functionality. </p>
First find and launch the application.
Then run the following script (<a href="code/example2.py">Example 2</a>)
<code><pre>
from watsup.winGuiAuto import findControl,setEditText, findTopWindow,clickButton
import os
import os.path
FILENAME='atestfile.txt'
def main():
# delete any occurrence of this file from the disk
if os.path.exists(FILENAME):
os.remove(FILENAME)
form=findTopWindow(wantedText='Simple Form')
button=findControl(form,wantedText='Create file')
editbox=findControl(form,wantedClass='TEdit')
# enter a filename:
setEditText(editbox,[FILENAME])
print 'clicking button to create file'
clickButton(button)
# now check that the file is there
if os.path.exists(FILENAME):
print 'file %s is present' %FILENAME
else:
print "file %s isn't there" % FILENAME
if __name__=='__main__':
main()
</pre>
</code>
<h3>Example 3 - automating program launch and termination</h3>
<p>It's a bit tedious having to start and close the application each time.
<a href="code/example3.py">Example 3</a> launches the application,
if it isn't already running, and terminates it on
completion of the test</p>
<code><pre>
from watsup.launcher import launchApp,terminateApp
from watsup.winGuiAuto import findTopWindows
import example2
# find an instance of SimpleForm. If one isn't there, launch it
forms=findTopWindows(wantedText='Simple Form')
if forms:
form=forms[0]
else:
form=launchApp('simple.exe',wantedText='Simple Form')
example2.main()
# and terminate the form
terminateApp(form)
</pre></code>
launchApp starts the application in a separate thread,
and looks for a window with caption containing "Simple Form",
returning the window handle of the recovered form.
terminateApp attempts to close the form, by trying to activate menu item File-Exit, or, failing that,
sending Alt + F4.
<H3>Example 4 - finding windows and controls</H3>
<p>In building scripts, we need to be able to find the class and/or text of the many windows and controls
to be investigated or invoked.</p>
<p>In the tools directory, there's a tool - ShowWindows.bat - to assist us with this.</p>
<img src="images/ShowWindows1.jpg" alt="Show Windows 1" />
<p>Clicking the "Register" button persists information about
the existing windows running on the system (and it tells you how many, FYI).
Clicking the "Find new" button will report all non-trivial windows and all their
constituent controls which have appeared in the windows environment
since the last "Register" click.
So to test our program simple.exe, launch ShowWindows, click Register.
Then launch simple.exe and
and click the Find New button.
The associated text box shows n/m, where m is the total number of new windows found,
and n is the number of those which are significant (ie have any controls) and are reported. </p>
<img src="images/ShowWindows2.jpg" alt="Show Windows 2" />
<H2>Performance Tests</H2>
<p>Performance tests, in this definition, are single-client scripts,
which are similar in operation to the functional tests above,
but for which certain steps of the tests
must either be done within an acceptable timeframe ("CHECKING")
and/or the time taken for those steps
should be recorded for subsequent analysis ("RECORDING").</p>
<p>WATSUP provides a simple mechanism to add such tests to existing functional tests.
In examples 4a & 4b, we launch almost identical applications,
perform.exe and perform2.exe respectively.
When the button is clicked, the text "Finished" is written
to the edit box. The difference between the programs is that the former is
coded to wait for 1/2 second before "Finished"
appears; in the latter case, the delay is 1.5 seconds.</p>
<p>In both cases, we are setting a performance test that the process
take no more than 1 second.
Clearly, we should expect example 4a to be ok and example 4b to fail.
</p>
<p>So we have <a href="code/example4a.py">Example 4a</a></p>
<code><pre>
from example4 import main
main('perform.exe','Performance Form 1')
</pre></code>
<p>and <a href="code/example4b.py">Example 4b</a></p>
<code><pre>
from example4 import main
main('perform2.exe','Performance Form 2')
</pre></code>
<p>which reference <a href="code/example4.py">Example 4</a>:</p>
<code><pre>
from watsup.launcher import launchApp,terminateApp
from watsup.winGuiAuto import findTopWindows, findControl,getEditText,clickButton
from watsup.performance import PerformanceCheck,PerformanceCheckError
from time import sleep,time
def main(myExecutable,myWantedText):
# find an instance of SimpleForm. If one isn't there, launch it
forms=findTopWindows(wantedText=myWantedText)
if forms:
form=forms[0]
else:
form=launchApp(myExecutable,wantedText=myWantedText)
button=findControl(form,wantedText='Click me')
editbox=findControl(form,wantedClass='TEdit')
#start a performance check instance
p=PerformanceCheck()
clickButton(button)
# belts and braces to avoid infinite waiting!
maxWaitTime=2.0
startTime=time()
while time()-startTime&lt;maxWaitTime:
t=getEditText(editbox)
if t:
break
else:
sleep(0.1)
else:
raise Exception,'Failed to get value after maxWaitTime of %s secs' % maxWaitTime
try:
try:
#do the check/recording step, identifying this step with the wantedtext
p.check(myWantedText,1.0)
except PerformanceCheckError,e:
print '** Failed: %s' % e
# and terminate the form
finally:
terminateApp(form)
if __name__=='__main__':
print ' please run example4a or 4b'
</pre></code>
<h4>Key points in example4.py</h4>
<p>Immediately prior to clicking the button, we establish a PerformanceCheck instance,
which, among other things, establises a timer.
Every time the check() method is called on a PerformanceCheck instance, the following occurs:</p>
<p>Also if we are CHECKING (the default condition),
that time is checked against the number of seconds added as the
second parameter.
If the elapsed time exceeds the value of the 2nd parameter, an exception is raised.</p>
<p>So in example4.py, we see that the check method requires a 1 second acceptance.
Hence example4a succeeds and example4b fails.
</p>
<H2>Regression Testing</H2>
<p>Functional and performance scripts such as those above are
immediately useable within a test framework -
the excellent python unit test framework is highly recommended
(unittest.py, which comes along with your python installation).
This then enables the tester to develop complete regression tests
involving any combination of Functional & Performance testing. </p>
<p>For an example of the use of functional and performance tests within
the unit test framework, run the program framework.bat in the tools subdirectory.
This will launch a nice user interface from which sets of test cases can be run:
</p>
<img src="images/framework1.jpg">
<p>Select the File menu option, and you have the option to load files which
contain testcases, or directories/directory trees. If select the "Load files"
option, and navigate up one directory, and then down through examples and the unittests
directories, you should find, and select, exampleTests.py.
The Framework program examines the selected file(s) and extracts the TestCases,
presents in the top frame</p>
<img src="images/framework2.jpg">
<p>Selecting Menu option Testing - Run Tests (or the long button labelled Run Tests),
will cause each test to be run; success or failure is shown in the lower frame</p>
<img src="images/framework3.jpg">
<p>For more information on python's unit test module, refer to the python documentation.
Finally, it is worth noting that this framework will work with any unit tests, not just
those specifically testing windows application, so you can test
elements of the logic that you have written in other applications.
<H2>Downloads</H2>
Download <a href="../downloads/watsup-0.4.zip">watsup</a> here
<p>(This package should be unzipped in the site-packages directory in your python installation)</p>
<H3> Dependencies</H3>
<ul>
<li><a href="http://www.python.org/download"> Python</a> (version at least 2.3)</li>
<li><a href="http://sourceforge.net/projects/pywin32">pywin32 </a></li>
<li><a href="http://sourceforge.net/project/showfiles.php?group_id=71702">ctypes </a></li>
<li><a href="http://www.rutherfurd.net/python/sendkeys/#binaries" >SendKeys </a></li>
<li><a href="http://www.wxpython.org/download.php" >wxPython library</a> (for tools)</li>
</ul>
<h3>Credits</h3>
<p> The framework test tool was built from parts in unittestgui.py by Chris Liechti.
Much of the core functionality in WATSUP derives from the important work by
<a href="http://www.brunningonline.net/simon/python/index.html">Simon Brunning</a>,
who, in his module winGuiAuto.py provided concise, accessible mechanisms to
access and "click" windows controls & menus; Simon's work recognises the huge
contribution that Mark Hammond has made to the python community (and wider)
in providing pywin32, comprehensive win32 API modules for python.
</p>
Dr Tim Couper<br/>
<a href="mailto:timc@tizmoi.net">timc@tizmoi.net</a>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -1,7 +0,0 @@
body {font-family: "Trebuchet MS", Verdana, sans-serif}
code {font: Courier New; size 12}
h1 {color: navy}
h2 {color: navy}
h3 {color: navy}
h4 {color: navy}

View file

@ -1,116 +0,0 @@
""" launch & terminate a windows application
"""
# Author : Tim Couper - tim@2wave.net
# Date : 22 July 2004
# Version : 1.0
# Copyright : Copyright TAC Software Ltd, under Python-style licence.
# Provided as-is, with no warranty.
# Notes : Requires Python 2.3, win32all, ctypes, SendKeys & winGuiAuto
import os.path
import threading
import win32gui
#import win32api
#import win32con
import SendKeys
from watsup.utils import WatsupError
from watsup.winGuiAuto import findTopWindow,findTopWindows, \
WinGuiAutoError,activateMenuItem
MENU_TERMINATIONS=[('file', 'exit'),('file', 'quit')]
def launchApp(program,wantedText=None,wantedClass=None,verbose=False):
global p
p=AppThread(program,verbose)
p.start()
try:
return findTopWindow(wantedText=wantedText,wantedClass=wantedClass)
except WinGuiAutoError,e:
pass
# find all the Windows that are lurking about, and terminate them
# this I can't do until terminateApp can find a window and bring it to focus
# for now, find one top window and return that; if there are none,
# it should raise an exception
try:
return findTopWindows(wantedText=wantedText,wantedClass=wantedClass)[0]
except IndexError:
raise WatsupError,'Failed to find any windows'
class AppThread(threading.Thread):
def __init__(self,program,verbose=False):
threading.Thread.__init__(self,name=program)
self.program=program
self.verbose=verbose
def run(self):
"""main control loop"""
#check the given program exists, and is a file:
# if the program has no type, make it .exe:
if os.path.splitext(self.program)[1]=='':
prog='%s.exe' % self.program
else:
prog=self.program
prog=os.path.abspath(prog)
if os.path.isfile(prog):
# launch the new application in a separate thread
if self.verbose:
print '..launching "%s"' % prog
os.system('"%s"' % prog)
else:
print 'AppThread: program not found: "%s"\n' % prog
if self.verbose:
print '..terminating "%s"' % prog
def terminateApp(hwnd=None):
"""Terminate the application by:
If there's a file-exit menu, click that
If there's a file-Quit menu, click that
Otherwise, send Alt-F4 to the current active window
** it's the calling program responsibility to get the right active window
"""
if hwnd:
for fileExit in MENU_TERMINATIONS:
try:
activateMenuItem(hwnd, fileExit)
return
except WinGuiAutoError:
pass
# blast the current window with ALT F4 ..
SendKeys.SendKeys("%{F4}")
#win32gui.PumpWaitingMessages()
## from watsup.winGuiAuto import findControl,findTopWindows
## topHs=findTopWindows(wantedText='MyTestForm')
## for topH in topHs:
## #hwnd=findControl(topH)
## a=win32gui.findWindow(topH)
## print a, topH
## win32gui.SetFocus(topH)
## win32gui.PumpWaitingMessages()
## SendKeys.SendKeys("%{F4}")
##
## if not hwnd and (wantedText or wantedClass):
## hwnds=findTopWindows(wantedText=wantedText,wantedClass=wantedClass)
## elif hwnd:
## hwnds=[hwnd]
##
## if hwnds:
## for hwnd in hwnds:
## #win32gui.SetFocus(hwnd)
## #win32gui.SetActiveWindow(hwnd)
## # mm the above don;t work, perhaps because the
## # window is actually to be found in another thread
##
## SendKeys.SendKeys("%{F4}")
if __name__=='__main__':
pass

View file

@ -1,181 +0,0 @@
# Author : Tim Couper - timc@tizmoi.net
# Date : 22 July 2004
# Version : 1.0
# Copyright : Copyright TAC Software Ltd, under Python-style licence.
# Provided as-is, with no warranty.
from time import time,ctime
import sys
import cPickle
#-------------------------------------------------------------------------------
# hidden globals
_PerformanceDataThisRun=None
_PerformanceDataStore=None
_doPerformanceChecks=True
_recordPerformanceData=False
#-------------------------------------------------------------------------------
# externals
def isRecording():
return _recordPerformanceData
def isChecking():
return _doPerformanceChecks
def doRecording(doIt=True):
global _recordPerformanceData
_recordPerformanceData=doIt
def doChecking(doIt=True):
global _doPerformanceChecks
_doPerformanceChecks=doIt
def onOff(bool):
if bool:
return 'On'
else:
return 'Off'
def getPerformanceDataThisRun():
"returns the Performance data for this run of the tests"
global _PerformanceDataThisRun
if _PerformanceDataThisRun==None:
# create an instance, and load up what was already there
_PerformanceDataThisRun=PerformanceDataThisRun()
return _PerformanceDataThisRun
def getPerformanceDataStore():
"returns the all Performance data for all runs of the tests"
global _PerformanceDataStore
if _PerformanceDataStore==None:
# create an instance, and load up what was already there
_PerformanceDataStore=PerformanceDataStore()
try:
_PerformanceDataStore=cPickle.load(open(_PerformanceDataStore.filename))
except IOError,EOFError:
pass
return _PerformanceDataStore
PERFORMANCE_STORE_FILENAME='PerformanceDataStore.dat'
class PerformanceDataCollectionError(Exception): pass
class PerformanceDataCollection(dict):
def __str__(self):
lines=[]
keys=self.keys()
keys.sort()
for msg in keys:
lines.append(str(msg))
pdiList=self[msg]
pdiList.sort()
for pdi in pdiList:
lines.append(' %s' % pdi)
return '\n'.join(lines)
#class PerformanceDataThisRunError(Exception): pass
class PerformanceDataThisRun(PerformanceDataCollection): pass
class PerformanceDataStoreError(Exception): pass
class PerformanceDataStore(PerformanceDataCollection):
'performs persistent store of PerformanceDataItems'
def __init__(self, filename=PERFORMANCE_STORE_FILENAME):
self.filename=filename
def addItem(self,msg,pdi):
'adds a pdi item to items, in the right place'
# msg must be immutable
if isinstance(pdi,PerformanceDataItem):
if not self.has_key(msg):
self[msg]=[]
self[msg].append(pdi)
else:
e='addItem can only accept PerformanceDataItem instances (%s: %s)' % (msg,str(pdi))
raise PerformanceDataStoreError,e
#-------------------------------------------------------------------------------
class PerformanceCheckError(AssertionError): pass
class PerformanceCheck(object):
def __init__(self):
self.reset()
def reset(self):
self.startTime=time()
def check(self,msg,maxDelay=None):
res=time()-self.startTime
# if we're storing the data
if isRecording():
pdi=PerformanceDataItem(self.startTime,res,maxDelay)
pd=getPerformanceDataThisRun()
if not pd.has_key(msg):
pd[msg]=[] #list of PerformanceData instances, we hope
# add it to the current data being processed
pd[msg].append(pdi)
# and add it to the store that we're going to persist
pds=getPerformanceDataStore()
pds.addItem(msg,pdi)
cPickle.dump(pds,open(pds.filename,'w'))
# if we're acting on performance tests:
if isChecking() and maxDelay<>None and (res>float(maxDelay)):
res=round(res,2)
msg='%s [Delay > %s secs (%s secs)]' % (msg,maxDelay,res)
raise PerformanceCheckError,msg
class PerformanceDataItem(object):
"holds a result (as elapsed time value in secs) and the time of invocation"
def __init__(self,startTime,elapsedTime,maxDelay=None):
self.startTime=startTime # time of run
self.elapsedTime=round(elapsedTime,2) # elapsed time in secs
self.maxDelay=maxDelay # user defined delay for check
# if None, then there is no upper threshhold
def __str__(self):
if self.maxDelay==None:
ok='OK'
md=''
else:
if self.elapsedTime<=self.maxDelay:
ok='OK'
else:
ok='Fail'
md= ' (%s)' % self.maxDelay
return '%s: %s%s %s' % (ctime(self.startTime), self.elapsedTime, md, ok)
def __cmp__(self,other):
if self.startTime<other.startTime:
return -1
elif self.startTime>other.startTime:
return 1
else:
return 0
def nicePrint(filename=sys.stdout):
def wout(f,text):
f.write('%s\n' % text)
if filename==sys.stdout:
f=sys.stdout
else:
f=open(filename,'w')
## from watsup.timedunittest import getPerformanceDataStore,getPerformanceDataThisRun, \
## isRecording
if isRecording():
for func in (getPerformanceDataThisRun,getPerformanceDataStore):
fn=func.__name__
wout(f,'\n%s\n%s\n' % (fn,'-'*len(fn)))
wout(f,str(func()))

View file

@ -1,279 +0,0 @@
# Author : Tim Couper - tim@2wave.net
# Date : 22 July 2004
# Version : 1.0
# Copyright : Copyright TAC Software Ltd, under Python-style licence.
# Provided as-is, with no warranty.
from unittest import TestCase
from time import time,sleep,ctime
import sys
if sys.platform=='win32':
import win32gui
def pump():
# clear down any waiting messages (it all helps)
win32gui.PumpWaitingMessages()
else:
pump=None
###-------------------------------------------------------------------------------
### hidden globals
##
##_PerformanceDataThisRun=None
##_PerformanceDataStore=None
##_doPerformanceChecks=True
##_recordPerformanceData=True
###-------------------------------------------------------------------------------
##
###-------------------------------------------------------------------------------
### externals
##
##def isRecording():
## return _recordPerformanceData
##
##def isChecking():
## return _doPerformanceChecks
##
##def doRecording(doIt=True):
## global _recordPerformanceData
## _recordPerformanceData=doIt
##
##def doChecking(doIt=True):
## global _doPerformanceChecks
## _doPerformanceChecks=doIt
##
##def getPerformanceDataThisRun():
## "returns the Performance data for this run of the tests"
## global _PerformanceDataThisRun
## if _PerformanceDataThisRun==None:
## # create an instance, and load up what was already there
## _PerformanceDataThisRun=PerformanceDataThisRun()
## return _PerformanceDataThisRun
##
##def getPerformanceDataStore():
## "returns the all Performance data for all runs of the tests"
## global _PerformanceDataStore
## if _PerformanceDataStore==None:
## # create an instance, and load up what was already there
## _PerformanceDataStore=PerformanceDataStore()
## try:
## _PerformanceDataStore=cPickle.load(open(_PerformanceDataStore.filename))
## except IOError,EOFError:
## pass
## return _PerformanceDataStore
##
##PERFORMANCE_STORE_FILENAME='PerformanceDataStore.dat'
##
##class PerformanceDataCollectionError(Exception): pass
##class PerformanceDataCollection(dict):
## def __str__(self):
## lines=[]
## keys=self.keys()
## keys.sort()
## for msg in keys:
## lines.append(str(msg))
## pdiList=self[msg]
## pdiList.sort()
## for pdi in pdiList:
## lines.append(' %s' % pdi)
## return '\n'.join(lines)
##
###class PerformanceDataThisRunError(Exception): pass
##class PerformanceDataThisRun(PerformanceDataCollection): pass
##
##class PerformanceDataStoreError(Exception): pass
##class PerformanceDataStore(PerformanceDataCollection):
## 'performs persistent store of PerformanceDataItems'
## def __init__(self, filename=PERFORMANCE_STORE_FILENAME):
## self.filename=filename
##
## def addItem(self,msg,pdi):
## 'adds a pdi item to items, in the right place'
## # msg must be immutable
## if isinstance(pdi,PerformanceDataItem):
## if not self.has_key(msg):
## self[msg]=[]
## self[msg].append(pdi)
## else:
## e='addItem can only accept PerformanceDataItem instances (%s: %s)' % (msg,str(pdi))
## raise PerformanceDataStoreError,e
##
###-------------------------------------------------------------------------------
##
##class PerformanceCheckError(AssertionError): pass
##
##class PerformanceCheck(object):
##
## def __init__(self):
## self.reset()
##
## def reset(self):
## self.startTime=time()
##
## def check(self,msg,maxDelay=None):
## res=time()-self.startTime
##
## # if we're storing the data
## if isRecording():
## pdi=PerformanceDataItem(self.startTime,res,maxDelay)
## pd=getPerformanceDataThisRun()
## if not pd.has_key(msg):
## pd[msg]=[] #list of PerformanceData instances, we hope
##
## # add it to the current data being processed
## pd[msg].append(pdi)
##
## # and add it to the store that we're going to persist
## pds=getPerformanceDataStore()
## pds.addItem(msg,pdi)
## cPickle.dump(pds,open(pds.filename,'w'))
##
## # if we're acting on performance tests:
## if isChecking() and maxDelay<>None and (res>float(maxDelay)):
## res=round(res,2)
## msg='%s [Delay > %s secs (%s secs)]' % (msg,maxDelay,res)
## raise PerformanceCheckError,msg
##
##class PerformanceDataItem(object):
## "holds a result (as elapsed time value in secs) and the time of invocation"
## def __init__(self,startTime,elapsedTime,maxDelay=None):
## self.startTime=startTime # time of run
## self.elapsedTime=round(elapsedTime,2) # elapsed time in secs
## self.maxDelay=maxDelay # user defined delay for check
## # if None, then there is no upper threshhold
##
## def __str__(self):
## if self.maxDelay==None:
## ok='OK'
## md=''
## else:
## if self.elapsedTime<=self.maxDelay:
## ok='OK'
## else:
## ok='Fail'
## md= ' (%s)' % self.maxDelay
##
## return '%s: %s%s %s' % (ctime(self.startTime), self.elapsedTime, md, ok)
##
## def __cmp__(self,other):
## if self.startTime<other.startTime:
## return -1
## elif self.startTime>other.startTime:
## return 1
## else:
## return 0
##
maxWaitSecsDefault=3.0
retrySecsDefault=0.2
class TimedOutError(AssertionError): pass
class TimedTestCase(TestCase):
# extends the functionality of unittest.TestCase to
# allow multiple attempts at assertion, failif, for
# a specific length of time. If the test is not successful by the time
# that Then it fails
def __init__(self, methodName='runTest',maxWaitSecsDefault=1.0,retrySecsDefault=0.2):
TestCase.__init__(self,methodName)
self.maxWaitSecsDefault=float(maxWaitSecsDefault)
self.retrySecsDefault=float(retrySecsDefault)
def doTimeTest(self,func,*args):
# remove the last 2 args, as they're out timing values:
maxWaitSecs=args[-2]
retrySecs=args[-1]
args=args[:-2]
if maxWaitSecs==None:
maxWaitSecs=self.maxWaitSecsDefault
else:
try:
maxWaitSecs=float(maxWaitSecs)
except TypeError,e:
e='%s (maxWaitSecs "%s")' % (e,maxWaitSecs)
raise TypeError,e
if retrySecs==None:
retrySecs=self.retrySecsDefault
else:
try:
retrySecs=float(retrySecs)
except TypeError,e:
e='%s (retrySecs "%s")' % (e,retrySecs)
raise TypeError,e
retrySecs=max(0,retrySecs-.001) # allow for the pump,etc below
t=time()
while (time()-t)<maxWaitSecs :
try:
func(self,*args)
break
except self.failureException:
if pump:
pump()
sleep(retrySecs)
else:
# timed out, and still not worked!
try:
func(self,*args)
except self.failureException,e:
raise TimedOutError,e
def fail(self,msg=None,maxWaitSecs=None,retrySecs=None):
self.doTimeTest(TestCase.fail,maxWaitSecs,retrySecs)
def failIf(self, expr, msg=None,maxWaitSecs=None,retrySecs=None):
"Fail the test if the expression is true."
self.doTimeTest(TestCase.failIf,expr,msg,maxWaitSecs,retrySecs)
def failUnless(self, expr, msg=None,maxWaitSecs=None,retrySecs=None):
"""Fail the test unless the expression is true."""
self.doTimeTest(TestCase.failUnless,expr,msg,maxWaitSecs,retrySecs)
def failUnlessEqual(self, first, second, msg=None,maxWaitSecs=None,retrySecs=None):
"""Fail if the two objects are unequal as determined by the '=='
operator.
"""
self.doTimeTest(TestCase.failUnlessEqual,first,second,msg,maxWaitSecs,retrySecs)
def failIfEqual(self, first, second, msg=None):
"""Fail if the two objects are equal as determined by the '=='
operator.
"""
self.doTimeTest(TestCase.failIfEqual,first,second,msg,maxWaitSecs,retrySecs)
def failUnlessAlmostEqual(self, first, second, places=7, msg=None):
"""Fail if the two objects are unequal as determined by their
difference rounded to the given number of decimal places
(default 7) and comparing to zero.
Note that decimal places (from zero) is usually not the same
as significant digits (measured from the most signficant digit).
"""
self.doTimeTest(TestCase.failUnlessAlmostEqual,first,second,places,msg,maxWaitSecs,retrySecs)
def failIfAlmostEqual(self, first, second, places=7, msg=None):
"""Fail if the two objects are equal as determined by their
difference rounded to the given number of decimal places
(default 7) and comparing to zero.
Note that decimal places (from zero) is usually not the same
as significant digits (measured from the most signficant digit).
"""
self.doTimeTest(TestCase.failIfAlmostEqual,first,second,places,msg,maxWaitSecs,retrySecs)
assertEqual = assertEquals = failUnlessEqual
assertNotEqual = assertNotEquals = failIfEqual
assertAlmostEqual = assertAlmostEquals = failUnlessAlmostEqual
assertNotAlmostEqual = assertNotAlmostEquals = failIfAlmostEqual
assert_ = failUnless

View file

@ -1 +0,0 @@
pythonw wxShowWindows.py

View file

@ -1,96 +0,0 @@
import unittest
import os.path, imp, os
from types import ListType, TupleType
def buildSuite(paths,recurse_directories=False):
# returns a suite of TestCase classes from the list of paths,
# These may be files and directories. If directories,
# there's the opportunity to recurse down all
def visit(args,dirname,allnames):
for aname in allnames:
fullname=os.path.join(dirname,aname)
if os.path.isdir(fullname):
res=buildSuitesFromDirectory(fullname)
if res:
suites.extend(res)
#ensure paths is a list:
if type(paths) not in (ListType, TupleType):
paths=[paths]
suites=[]
for path in paths:
if os.path.isfile(path):
res=buildSuiteFromFile(path)
if res:
suites.append(res)
elif os.path.isdir(path):
#print 'Directory: %s' % path
# find all the TestCases in this directory:
res=buildSuitesFromDirectory(path)
if res:
suites.extend(res)
if recurse_directories:
os.path.walk(path,visit,suites)
t=unittest.TestSuite()
for suite in suites:
for test in suite._tests:
t.addTest(test)
if len(t._tests):
return t
else:
return None
def buildSuiteFromFile(filename):
modname, modtype = os.path.splitext(os.path.basename(filename))
if modtype.lower() == '.py':
moduleToTest = imp.load_source(modname, filename, file(filename))
#elif modtype.lower() in {'.pyc':0, '.pyo':0}:
# moduleToTest = imp.load_compiled(modname, filename, file(filename, 'rb'))
else:
return None
suite= unittest.defaultTestLoader.loadTestsFromModule(moduleToTest)
for test in suite._tests: # tests is a TestSuite class
# remove any which are null:
if len(test._tests)==0:
suite._tests.remove(test)
if len(suite._tests): #not interested if no tests!
return suite
else:
return None
def buildSuitesFromDirectory(dirname):
filenames=os.listdir(dirname)
suites=[]
for filename in filenames:
fullname=os.path.join(dirname,filename)
res=buildSuiteFromFile(fullname)
if res:
suites.append(res)
return suites
if __name__=='__main__':
dir='c:/MyProducts/watsup/examples/unittests'
fs=[]
for fname in ('ExampleTestsAgain.py','ExampleTests.py'):
fs.append(os.path.join(dir,fname))
myfile=os.path.abspath( __file__)
#print buildSuite(myfile)
mydir=os.path.split(myfile)[0]
suite= buildSuite(dir,True)
print '------------'
print suite._tests

View file

@ -1,42 +0,0 @@
""" Lists the windowText and className for a given executable on the system.
Usage:
c:>findAppControls.py example/example1
c:>findAppControls.py -v example/example1
- this does it in verbose mode
"""
# Author : Tim Couper - tim@tizmoi.net
# Date : 1 August 2004
# Copyright : Copyright TAC Software Ltd, under Python-like licence.
# Provided as-is, with no warranty.
# Notes : Requires watsup
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-v", "--verbose",
action="store_true", dest="verbose", default=False,
help="print verbose messages")
(options, args) = parser.parse_args()
if len(args)<1:
print 'Usage: findAppControls.py <path_to_executable>'
else:
from watsup.AppControls import AppControls
try:
print 'passing',args[0]
a=AppControls(args[0],verbose=options.verbose)
a.run()
except:
import sys
print '\n** %s **\n' % sys.exc_info()[1]
import traceback
traceback.print_tb(sys.exc_info()[2])

View file

@ -1,127 +0,0 @@
#Boa:Frame:ShowWindows
# Author : Tim Couper - tim@tizmoi.net
# Date : 1 August 2004
# Copyright : Copyright TAC Software Ltd, under Python-like licence.
# Provided as-is, with no warranty.
# Notes : Requires watsup, wxPython
from wxPython.wx import *
from wxPython.lib.anchors import LayoutAnchors
from watsup.tools.showWindows import findAll,findNew,readPickle
from watsup.winGuiAuto import dumpWindow
from watsup.utils import tupleHwnd
import pprint
def create(parent):
return ShowWindows(parent)
[wxID_SHOWWINDOWS, wxID_SHOWWINDOWSFINDNEW, wxID_SHOWWINDOWSNEWWINDOWS,
wxID_SHOWWINDOWSPANEL1, wxID_SHOWWINDOWSREGISTER, wxID_SHOWWINDOWSREGISTERED,
wxID_SHOWWINDOWSTEXT,
] = map(lambda _init_ctrls: wxNewId(), range(7))
class ShowWindows(wxFrame):
def _init_ctrls(self, prnt):
# generated method, don't edit
wxFrame.__init__(self, id=wxID_SHOWWINDOWS, name='ShowWindows',
parent=prnt, pos=wxPoint(424, 184), size=wxSize(456, 433),
style=wxMINIMIZE_BOX | wxSTATIC_BORDER | wxCAPTION | wxSYSTEM_MENU,
title='ShowWindows 1.0')
self.SetClientSize(wxSize(448, 399))
self.SetToolTipString('ShowWindow')
self.Center(wxBOTH)
self.Enable(True)
self.SetSizeHints(-1, -1, -1, -1)
self.SetThemeEnabled(False)
self.panel1 = wxPanel(id=wxID_SHOWWINDOWSPANEL1, name='panel1',
parent=self, pos=wxPoint(0, 350), size=wxSize(450, 50),
style=wxTAB_TRAVERSAL)
self.panel1.SetConstraints(LayoutAnchors(self.panel1, True, True, False,
False))
self.Register = wxButton(id=wxID_SHOWWINDOWSREGISTER, label='Register',
name='Register', parent=self.panel1, pos=wxPoint(32, 13),
size=wxSize(75, 23), style=0)
self.Register.SetToolTipString('Register all windows info')
EVT_BUTTON(self.Register, wxID_SHOWWINDOWSREGISTER,
self.OnRegisterButton)
self.FindNew = wxButton(id=wxID_SHOWWINDOWSFINDNEW, label='Find New',
name='FindNew', parent=self.panel1, pos=wxPoint(304, 13),
size=wxSize(75, 23), style=0)
EVT_BUTTON(self.FindNew, wxID_SHOWWINDOWSFINDNEW, self.OnFindNewButton)
self.Text = wxTextCtrl(id=wxID_SHOWWINDOWSTEXT, name='Text',
parent=self, pos=wxPoint(0, 0), size=wxSize(450, 350),
style=wxRAISED_BORDER | wxTE_WORDWRAP | wxTE_MULTILINE, value='')
self.Registered = wxTextCtrl(id=wxID_SHOWWINDOWSREGISTERED,
name='Registered', parent=self.panel1, pos=wxPoint(110, 16),
size=wxSize(40, 16), style=wxTE_CENTER | wxTE_READONLY, value='')
self.Registered.SetToolTipString('No of windows registered on system')
self.Registered.SetBackgroundColour(wxColour(175, 175, 175))
self.NewWindows = wxTextCtrl(id=wxID_SHOWWINDOWSNEWWINDOWS,
name='NewWindows', parent=self.panel1, pos=wxPoint(382, 16),
size=wxSize(40, 16), style=wxTE_CENTER | wxTE_READONLY, value='')
self.NewWindows.SetToolTipString('No of new windows found')
self.NewWindows.SetBackgroundColour(wxColour(175, 175, 175))
def __init__(self, parent):
self._init_ctrls(parent)
#load up the last value for your info
ws=readPickle()
if ws:
self.Registered.SetValue(str(len(ws)))
def OnRegisterButton(self, event):
ws=findAll()
self.Registered.SetBackgroundColour(wxColour(255, 255, 255))
self.Registered.SetValue(str(len(ws)))
def OnFindNewButton(self, event):
ws=findNew()
self.NewWindows.SetBackgroundColour(wxColour(255, 255, 255))
# write the details to the text box
withControls=self.writeNewWindows(ws)
self.NewWindows.SetValue('%s/%d' % (withControls,len(ws)))
def writeNewWindows(self,ws):
noControls=0
withControls=0
txt=[]
for w in ws:
dw=dumpWindow(w)
if not dw:
noControls+=1
continue
t=tupleHwnd(w)
# don't bother with ShowWindows application:
wclass=t[2]
wtext=t[1]
if wclass=='wxWindowClass' and wtext.startswith('ShowWindows'):
noControls+=1
continue
if wtext:
wtext="%s" % wtext
else:
wtext=''
withControls+=1
# write the heading window
#txt.append('%d. %s %s' % (withControls,wclass,wtext))
txt.append('%d. %s' % (withControls,str(list(t))))
txt.append(pprint.pformat(dw))
txt.append('')
self.Text.SetValue('\n'.join(txt))
return withControls

View file

@ -1,4 +0,0 @@
[main]
checking = True
current_directory = C:\Python23\Lib\site-packages\watsup\examples\unittests

View file

@ -1,34 +0,0 @@
# logic for wxShowWindows
# Author : Tim Couper - timc@tizmoi.net
# Date : 1 August 2004
# Copyright : Copyright TAC Software Ltd, under Python-like licence.
# Provided as-is, with no warranty.
# Notes : Requires watsup
import cPickle
from watsup.AppControls import findNewTopWindows
from watsup.winGuiAuto import dumpWindow
from watsup.utils import dumpHwnd
import os.path
from types import ListType
PICKLE_FILE='findall.pkl'
def readPickle(pickle_file=PICKLE_FILE):
#reads the list in the pickle file if possible
if os.path.exists(pickle_file):
return cPickle.load(open(pickle_file))
else:
return []
def findAll(pickle_file=PICKLE_FILE):
# get all the top windows:
res=findNewTopWindows()
cPickle.dump(res,open(pickle_file,'w'))
return res
def findNew(pickle_file=PICKLE_FILE):
# get all the top windows, and return any new ones
olds=readPickle(pickle_file)
return findNewTopWindows(olds)

View file

@ -1 +0,0 @@
python watsup_framework.py

View file

@ -1,653 +0,0 @@
# Author : Tim Couper - timc@tizmoi.net
# Date : 1 August 2004
# Copyright : Copyright TAC Software Ltd, under Python-like licence.
# Provided as-is, with no warranty.
# Notes : Requires wxPython,watsup
# Based heavily on unittestgui.py by Chris Liechti
import unittest, os, imp, time, traceback,sys
## import all of the wxPython GUI package
from wxPython.wx import *
from wxPython.grid import *
from watsup.performance import doChecking,onOff,isChecking
from watsup.tools.buildSuite import buildSuite
from time import ctime
BASE_TITLE="Watsup Framework"
INI_FILE='./runTests.ini'
from ConfigParser import ConfigParser,NoOptionError, NoSectionError
#specify editor, make it return immediately, otherwise the GUI will wait
#until its closed. on win use "start cmd", on un*x "cmd&"
EDITOR = r'start c:\tools\wscite\scite "%(filename)s" -goto:%(lineno)s'
def starteditor(filename, lineno=1):
if os.path.exists(filename):
os.system(EDITOR % {'filename':filename, 'lineno':lineno})
else:
wxMessageBox("Cannot locate sourcefile:\n%s" % filename, "Can't start editor...", wxOK)
#----------------------------------------------------------------
class GUITestResult(unittest.TestResult):
"""A test result class that can print formatted text results to a stream.
"""
separator1 = '=' * 70
separator2 = '-' * 70
def __init__(self, listview, progress, descriptions, verbosity):
unittest.TestResult.__init__(self)
self.listview = listview
self.showAll = verbosity > 1
self.descriptions = descriptions
self.progress = progress
self.testdescr = None
self.stream = sys.stdout
def getDescription(self, test):
if self.descriptions:
return test.shortDescription() or str(test)
else:
return str(test)
def startTest(self, test):
unittest.TestResult.startTest(self, test)
self.testdescr = self.getDescription(test)
if self.showAll:
self.stream.write(self.getDescription(test))
self.stream.write(" ... ")
def _correctFilename(self, filename):
if filename[-4:] in ('.pyc', '.pyo'):
return filename[:-1]
return filename
def addSuccess(self, test):
unittest.TestResult.addSuccess(self, test)
if self.showAll:
self.stream.write("ok\n")
self.listview.Append( (
'Ok',
self.testdescr,
'',
None,
None
) )
self.progress.tick()
def addError(self, test, err):
unittest.TestResult.addError(self, test, err)
if self.showAll:
self.stream.write("ERROR\n")
try:
filename = self._correctFilename(err[2].tb_frame.f_globals['__file__'])
except KeyError:
filename = None
lineno = err[2].tb_lineno
self.listview.Append( (
'Error',
self.testdescr,
traceback.format_exception(*err)[-1].rstrip(),
self._exc_info_to_string(err),
(filename, lineno)
) )
self.progress.tick()
def addFailure(self, test, err):
unittest.TestResult.addFailure(self, test, err)
if self.showAll:
self.stream.write("FAIL\n")
try:
filename = self._correctFilename(err[2].tb_frame.f_globals['__file__'])
except KeyError:
filename = None
lineno = err[2].tb_lineno
self.listview.Append( (
'Fail',
self.testdescr,
traceback.format_exception(*err)[-1].rstrip(),
self._exc_info_to_string(err),
(filename, lineno)
) )
self.progress.tick()
def printErrors(self):
if self.showAll:
self.stream.write("\n")
self.printErrorList('ERROR', self.errors)
self.printErrorList('FAIL', self.failures)
def printErrorList(self, flavour, errors):
for test, err in errors:
self.stream.write(self.separator1)
self.stream.write("\n%s: %s\n" % (flavour,self.getDescription(test)))
self.stream.write(self.separator2)
self.stream.write("\n%s\n" % err)
class GUITestRunner:
"""A test runner class that displays results in textual form.
It prints out the names of tests as they are run, errors as they
occur, and a summary of the results at the end of the test run.
"""
def __init__(self, listview, progress, stream=sys.stderr, descriptions=1, verbosity=2):
self.listview = listview
self.progress = progress
self.stream = unittest._WritelnDecorator(stream)
self.descriptions = descriptions
self.verbosity = verbosity
def _makeResult(self):
return GUITestResult(self.listview, self.progress, self.descriptions, self.verbosity)
def run(self, test):
"Run the given test case or test suite."
result = self._makeResult()
startTime = time.time()
test(result)
stopTime = time.time()
timeTaken = float(stopTime - startTime)
result.printErrors()
self.stream.writeln(result.separator2)
run = result.testsRun
self.stream.writeln("Ran %d test%s in %.3fs" %
(run, run == 1 and "" or "s", timeTaken))
self.stream.writeln()
if not result.wasSuccessful():
self.stream.write("FAILED (")
failed, errored = map(len, (result.failures, result.errors))
if failed:
self.stream.write("failures=%d" % failed)
if errored:
if failed: self.stream.write(", ")
self.stream.write("errors=%d" % errored)
self.stream.writeln(")")
else:
self.stream.writeln("OK")
return result
#----------------------------------------------------------------
def lastline(t):
t = t.rstrip()
n = t.rfind('\n')
if n >= 0:
return t[n+1:]
return t
#----------------------------------------------------------------
class ResultView(wxListCtrl):
PM_DETAIL = wxNewId()
PM_LOCATE = wxNewId()
def __init__(self, parent):
wxListCtrl.__init__(self, parent, -1,
style=wxLC_REPORT|wxSUNKEN_BORDER|wxLC_VIRTUAL|wxLC_VRULES,#|wxLC_HRULES,
size=(500,200))
self.InsertColumn(0, '-Ok-')
self.InsertColumn(1, 'Description')
self.InsertColumn(2, 'Details')
self.SetColumnWidth(0, 40)
w = self.GetSize()[0] - self.GetColumnWidth(0)
self.SetColumnWidth(1, w/2)
self.SetColumnWidth(2, w/2)
EVT_RIGHT_DOWN(self, self.OnRightClick)
EVT_LIST_ITEM_SELECTED(self, self.GetId(), self.OnItemSelected)
EVT_LIST_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated)
self.red = wxListItemAttr()
self.red.SetBackgroundColour(wxRED)
self.green = wxListItemAttr()
self.green.SetBackgroundColour(wxColour(200,255,200)) #light green
self.orange = wxListItemAttr()
self.orange.SetBackgroundColour(wxColour(255,200,200)) #light red
self.result = []
self.currentItem = None
EVT_SIZE(self, self.OnSize)
#node menu
self.nodemenu = wxMenu()
self.nodemenu.Append(self.PM_DETAIL, "Show Details")
#self.nodemenu.AppendSeparator()
#self.nodemenu.Append(self.PM_LOCATE, "Locate Source")
EVT_MENU(self, self.PM_DETAIL, self.OnDetail)
#EVT_MENU(self, self.PM_LOCATE, self.OnLocate)
def OnDetail(self, event=None):
if self.result[self.currentItem][3]:
wxMessageBox(self.result[self.currentItem][3], "Result details", wxOK)
def OnLocate(self, event=None):
filename, lineno = self.result[self.currentItem][4]
print "locate",filename, lineno
starteditor(filename, lineno)
def OnSize(self, event):
w = self.GetSize()[0] - self.GetColumnWidth(0) - 30
if w < 180: w = 180
self.SetColumnWidth(1, w/2)
self.SetColumnWidth(2, w/2)
def Append(self, item):
self.result.append(item)
self.SetItemCount(len(self.result))
#wxYield()
#self.Refresh()
def DeleteAllItems(self):
self.result = []
wxListCtrl.DeleteAllItems(self)
def OnItemSelected(self, event):
self.currentItem = event.m_itemIndex
def OnItemActivated(self, event):
self.currentItem = event.m_itemIndex
self.OnDetail()
def OnRightClick(self, event):
pt = event.GetPosition()
item, flags = self.HitTest(pt)
if not flags & wxLIST_HITTEST_NOWHERE:
if self.currentItem is not None:
self.SetItemState(self.currentItem, 0, wxLIST_STATE_SELECTED )
self.currentItem = item
self.SetItemState(item, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED )
if self.result[self.currentItem][3]:
self.PopupMenu(self.nodemenu, pt) #display popup menu
def OnCopyAsText(self, all=0):
res = []
item = -1;
while 1:
if all:
item = self.GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_DONTCARE);
else:
item = self.GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
text = '\t'.join(map(str, self.result[item]))
res.append(text)
if item == -1:
break
clip = wxClipboard()
clip.Open()
clip.SetData( wxTextDataObject('\n'.join(res)) )
clip.Close()
#---------------------------------------------------
# These methods are callbacks for implementing the
# "virtualness" of the list...
def OnGetItemText(self, row, col):
#print row, col
try:
return str(self.result[row][col])
except IndexError:
return ''
def OnGetItemImage(self, item):
return 0
def OnGetItemAttr(self, item):
if self.result[item][0] == 'Error':
return self.red
elif self.result[item][0] == 'Fail':
return self.orange
elif self.result[item][0] == 'Ok':
return self.green
return None
class TreeView(wxTreeCtrl):
PM_RUNSEL = wxNewId()
PM_LOCATE = wxNewId()
def __init__(self, *args, **kwargs):
wxTreeCtrl.__init__(self, *args, **kwargs)
EVT_LEFT_DCLICK(self, self.OnLeftDClick)
EVT_RIGHT_DOWN(self, self.OnRightClick)
EVT_RIGHT_UP(self, self.OnRightUp)
EVT_TREE_BEGIN_LABEL_EDIT(self, self.GetId(), self.OnBeginEdit)
EVT_TREE_END_LABEL_EDIT (self, self.GetId(), self.OnEndEdit)
#node menu
## self.nodemenu = wxMenu()
## self.nodemenu.Append(self.PM_RUNSEL, "Run selected")
## self.nodemenu.AppendSeparator()
## self.nodemenu.Append(self.PM_LOCATE, "Locate Source")
##
## EVT_MENU(self, self.PM_RUNSEL, self.OnRunSel)
## EVT_MENU(self, self.PM_LOCATE, self.OnLocate)
#don't allow editing of node names
def OnBeginEdit(self, event): event.Veto()
def OnEndEdit(self, event): event.Veto()
def OnLeftDClick(self, event):
## pt = event.GetPosition();
## item, flags = self.HitTest(pt)
## #print("OnLeftDClick: %s" % self.GetItemText(item))
## parent = self.GetItemParent(item)
## #self.SortChildren(parent)
event.Skip()
def OnRightClick(self, event):
pt = event.GetPosition()
item, flags = self.HitTest(pt)
if not flags & wxTREE_HITTEST_NOWHERE:
self.SelectItem(item)
#self.PopupMenu(self.nodemenu, pt) #display node menu
def OnRightUp(self, event):
pt = event.GetPosition();
item, flags = self.HitTest(pt)
#self.tree.EditLabel(item)
def OnCompareItems(self, item1, item2):
t1 = self.GetItemText(item1)
t2 = self.GetItemText(item2)
#print('compare: ' + t1 + ' <> ' + t2)
return cmp(t1,t2)
def OnRunSel(self, event):
pass
def OnLocate(self, event):
pass
#----------------------------------------------------------------
class ProgressBar(wxGauge):
def __init__(self, *args, **kwargs):
wxGauge.__init__(self, *args, **kwargs)
self.SetBezelFace(5)
self.SetShadowWidth(5)
self.increment = 1
def tick(self):
self.SetValue(self.GetValue() + self.increment)
#----------------------------------------------------------------
class Frame(wxFrame):
ID_BUTTON = wxNewId()
#M_NEW = wxNewId()
M_OPENFILES = wxNewId()
M_OPENDIR = wxNewId()
M_OPENTREE = wxNewId()
M_EXIT = wxNewId()
M_COPYALL = wxNewId()
M_COPYSEL = wxNewId()
M_RUN = wxNewId()
M_CHECKING = wxNewId()
def __init__(self, parent, id, title = BASE_TITLE):
# First, call the base class' __init__ method to create the frame
wxFrame.__init__(self, parent, id, title, wxPoint(100, 100), wxSize(100, 100))
#variables
self.suite = None
self.loadConfig()
#menu
menuBar = wxMenuBar()
menu = wxMenu()
#menu.Append(self.M_NEW, "&New")
menu.Append(self.M_OPENFILES, "&Load Test File(s)")
menu.Append(self.M_OPENDIR, "&Load Test Directory")
menu.Append(self.M_OPENTREE, "&Load Test Directory Tree")
menu.AppendSeparator()
menu.Append(self.M_EXIT, "E&xit")
#EVT_MENU(self, self.M_NEW, self.OnNew)
EVT_MENU(self, self.M_OPENFILES, self.OnMenuOpenFiles)
EVT_MENU(self, self.M_OPENDIR, self.OnMenuOpenDir)
EVT_MENU(self, self.M_OPENTREE, self.OnMenuOpenTree)
EVT_MENU(self, self.M_EXIT, self.OnCloseWindow)
menuBar.Append(menu, "&File");
menu = wxMenu()
menu.Append(self.M_COPYALL, "&Copy all results")
menu.Append(self.M_COPYSEL, "&Copy selected results")
EVT_MENU(self, self.M_COPYALL, self.OnCopyAll)
EVT_MENU(self, self.M_COPYSEL, self.OnCopySelection)
menuBar.Append(menu, "&Edit");
menu = wxMenu()
menu.Append(self.M_RUN, "&Run all")
menu.AppendSeparator()
menu.AppendCheckItem(self.M_CHECKING,"&Checking")
menu.Check(self.M_CHECKING,isChecking())
EVT_MENU(self, self.M_RUN, self.OnRun)
EVT_MENU(self, self.M_CHECKING, self.OnChecking)
self.testing_menu=menu
menuBar.Append(menu, "&Testing")
self.SetMenuBar(menuBar)
#GUI
panel = wxPanel(self, 0)
button = wxButton(panel, self.ID_BUTTON, "Run Tests" )
button.SetDefault()
EVT_BUTTON(panel, self.ID_BUTTON, self.OnRun )
self.tree = TreeView(panel, -1, size=(300,250), style=wxTR_HAS_BUTTONS | wxTR_EDIT_LABELS)# | wxTR_MULTIPLE
self.progress = ProgressBar(panel, -1, 100, style=wxGA_HORIZONTAL|wxGA_SMOOTH)
self.list = ResultView(panel)
sizer = wxBoxSizer(wxVERTICAL)
sizer.Add(self.tree, 1, wxEXPAND)
sizer.Add(button, 0, wxEXPAND)
sizer.Add(self.progress, 0, wxEXPAND)
sizer.Add(self.list, 1, wxEXPAND)
panel.SetAutoLayout(1)
panel.SetSizer(sizer)
sizer.Fit(panel)
basesizer = wxBoxSizer(wxVERTICAL)
basesizer.Add(panel, 1, wxEXPAND)
self.SetAutoLayout(1)
self.SetSizer(basesizer)
self.Fit()
#create a statusbar
sb = self.CreateStatusBar(1)
sb.SetStatusWidths([-1])
self.SetStatusText('Please load file(s), a directory or tree',0)
#update controls
self.OnNew()
def __del__(self):
# persist any values in .ini file
cfg=ConfigParser()
cfg.add_section('main')
cfg.set('main','current_directory',self.getCurrentDirectory())
cfg.set('main','checking',isChecking())
cfg.write(open(INI_FILE,'w'))
def loadConfig(self):
# load settings from the config file
cfg=ConfigParser()
cfg.read(INI_FILE)
self._currentDirectory='.'
try:
self.setCurrentDirectory(cfg.get('main','current_directory'))
except NoSectionError: # none of the other options will be available
return
except NoOptionError:
pass
try:
doChecking(cfg.getboolean('main','checking'))
except (NoOptionError, NoSectionError):
doChecking(True)
def UpdateTree(self, root=None, testlist=None):
if root is None:
root = self.root
if testlist is None and self.suite:
testlist = self.suite._tests #grmpf accessing a _ member, oh well
if root and testlist:
for testcase in testlist:
if isinstance(testcase, unittest.TestSuite):
testname=testcase._tests[0]
#str(testname): atestcasename (module.class)
# we want module.class here, with no brackets
# this should do it
label= str(testname).split('(')[1]
label=label[:-1]
child = self.tree.AppendItem(root, "%s (%d)" % (label,len(testcase._tests)))
self.tree.SetPyData(child, None)
self.UpdateTree(child, testcase._tests)
self.tree.SortChildren(child)
self.tree.Collapse(child)
else:
label="%s" % testcase
label=label.split('(')[0]
child = self.tree.AppendItem(root,label)
self.tree.SetPyData(child, None)
##self.tree.SetItemImage(child, idx2, wxTreeItemIcon_Expanded)
##self.tree.SetItemSelectedImage(child, idx3)
self.tree.Expand(root)
def OnRun(self, event=None):
""" """
if self.suite:
runner = GUITestRunner(self.list, self.progress)
self.SetStatusText('Running tests...',0)
ln = self.suite.countTestCases()
self.progress.SetValue(0)
self.progress.SetRange(ln)
self.list.DeleteAllItems()
result = runner.run(self.suite)
self.SetStatusText('Ran %d tests, %d errors, %d failures' % (ln, len(result.errors), len(result.failures)), 0)
else:
self.SetStatusText('No tests found', 0)
def OnMenuOpenFiles(self, event=None):
""" """
dlg = wxFileDialog(self, "Choose one or more modules",
self.getCurrentDirectory(),"",
'Python files (*.py)|*.py|All files (*.*)|*.*',
wxOPEN|wxMULTIPLE)
if dlg.ShowModal() == wxID_OK:
## for path in dlg.GetPaths():
## log.WriteText('You selected: %s\n' % path)
self.OnNew()
filenames = dlg.GetPaths() #dlg.GetPath()
self.setCurrentDirectory(dlg.GetDirectory())
self.suite=buildSuite(filenames)
#print self.suite
dlg.Destroy()
self.UpdateTree()
def OnMenuOpenDir(self,event=None):
self.loadDirectory('Choose the test directory')
def OnMenuOpenTree(self,event=None):
self.loadDirectory('Choose the top test directory',True)
def loadDirectory(self,msg,tree=False):
dlg=wxDirDialog(self,msg, self.getCurrentDirectory())
if dlg.ShowModal() == wxID_OK:
self.OnNew()
dir = dlg.GetPath()
self.setCurrentDirectory(dir)
self.suite=buildSuite(dir,tree)
#print self.suite
dlg.Destroy()
self.UpdateTree()
def OnNew(self, event=None):
self.list.DeleteAllItems()
self.tree.DeleteAllItems()
self.root = self.tree.AddRoot("Watsup Tests:")
self.tree.SetPyData(self.root, None)
self.progress.SetValue(0)
self.suite = None
#self.filename = None
def OnCopySelection(self,event=None):
self.list.OnCopyAsText()
def OnCopyAll(self,event=None):
self.list.OnCopyAsText(all=1)
def OnCloseWindow(self, event=None):
self.Destroy()
def OnChecking(self,event=None):
# toggle self._checking:
bool=not isChecking()
self.testing_menu.Check(self.M_CHECKING,bool)
doChecking(bool)
self.SetStatusText('Checking %s' % onOff(bool),0)
def getCurrentDirectory(self):
if os.path.isdir(self._currentDirectory):
return self._currentDirectory
else:
return '.'
def setCurrentDirectory(self,value):
if self._currentDirectory<>value:
if os.path.isdir(value):
self._currentDirectory=value
# update the title
if value=='.':
text=''
else:
text='(%s)' % value
self.SetTitle('%s %s'.strip() % (BASE_TITLE,text))
else:
self._currentDirectory='.'
#---------------------------------------------------------------------------
# Every wxWindows application must have a class derived from wxApp
class App(wxApp):
# wxWindows calls this method to initialize the application
def OnInit(self):
# Create an instance of our customized Frame class
frame = Frame(NULL, -1)
frame.Show(true)
# Tell wxWindows that this is our main window
self.SetTopWindow(frame)
# Return a success flag
return true
#---------------------------------------------------------------------------
if __name__ == "__main__":
app = App(0) # Create an instance of the application class
app.MainLoop() # Tell it to start processing events

View file

@ -1,29 +0,0 @@
#!/usr/bin/env python
#Boa:App:BoaApp
# Author : Tim Couper - tim@tizmoi.net
# Date : 1 August 2004
# Copyright : Copyright TAC Software Ltd, under Python-like licence.
# Provided as-is, with no warranty.
# Notes : Requires watsup,wxPython
from wxPython.wx import *
import fmShowWindows
modules ={'fmShowWindows': [1, 'Main frame of Application', 'fmShowWindows.py']}
class BoaApp(wxApp):
def OnInit(self):
wxInitAllImageHandlers()
self.main = fmShowWindows.create(None)
self.main.Show()
self.SetTopWindow(self.main)
return True
def main():
application = BoaApp(0)
application.MainLoop()
if __name__ == '__main__':
main()

View file

@ -1,37 +0,0 @@
""" watsup system utilities
"""
# Author : Tim Couper - timc@tizmoi.net
# Date : 22 July 2004
# Version : 1.0
# Copyright : Copyright TAC Software Ltd, under Python-style licence.
# Provided as-is, with no warranty.
# Notes : Requires win32all
import win32gui
import win32api
import sys
class WatsupError(Exception): pass
##def kill(pid):
## """kill function for Win32"""
## handle = win32api.OpenProcess(1, 0, pid)
## return (0 != win32api.TerminateProcess(handle, 0))
def dumpHwnd(hwnd):
t=list(tupleHwnd(hwnd))
t.reverse()
return '%s:"%s" (%d)' % tuple(t) #(win32gui.GetClassName(hwnd),win32gui.GetWindowText(hwnd),hwnd)
def tupleHwnd(hwnd):
return (hwnd,win32gui.GetWindowText(hwnd),win32gui.GetClassName(hwnd))
def pump():
win32gui.PumpWaitingMessages()
if __name__=='__main__':
pass

View file

@ -1,45 +0,0 @@
# provides a testing interface to web applications via the PAMIE module
# Author : Tim Couper - timc@tizmoi.net
# Date : 22 July 2004
# Version : 1.0
# Copyright : Copyright TAC Software Ltd, under Python-style licence.
# Provided as-is, with no warranty.
from cPAMIE import PAMIE
def findRunningIE():
from win32com.client import Dispatch
from win32gui import GetClassName
ShellWindowsCLSID = '{9BA05972-F6A8-11CF-A442-00A0C90A8F39}'
ShellWindows = Dispatch ( ShellWindowsCLSID )
# try to get an ie instance from the window
for shellwindow in ShellWindows :
if GetClassName ( shellwindow.HWND ) == 'IEFrame' :
return shellwindow
class WatsupIE(PAMIE):
def __init__(self,url=None, timeOut=1000, useExistingIfPossible=False):
self._ie=None
if useExistingIfPossible:
self._ie=findRunningIE()
if self._ie:
# this case can only arise if we've located a running IE;
# the code below should be everything else in PAMIE.__init__,
# apart from instantiation of the new ie instance:
if url:
self._ie.Navigate(url)
else:
self._ie.Navigate('about:blank')
self._timeOut = timeOut
self._ie.Visible = 1
else:
PAMIE.__init__(self,url,timeOut)

File diff suppressed because it is too large Load diff

View file

@ -1,264 +0,0 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
loops client agent interfaces.
$Id$
"""
from zope.interface import Interface, Attribute
class IAgent(Interface):
""" An agent watches its client, looks for resources to process,
and transfers these to its server.
"""
scheduler = Attribute('IScheduler instance.')
transporter = Attribute('The transporter to be used for transferring '
'objects.')
class IScheduler(Interface):
""" Manages jobs and cares that they are started at the appropriate
time.
"""
logger = Attribute('Logger instance to be used for recording '
'job execution and execution results.')
def schedule(job, startTime=None):
""" Register the job given for execution at the intended start
date/time (an integer timestamp) and return the job.
If the start time is not given schedulethe job for immediate
start. Return the start time with which the job has been
scheduled - this may be different from the start time
supplied.
"""
def getJobsToExecute(startTime=None):
""" Return a collection of jobs that are scheduled for execution at
or before the date/time given.
If startTime is None the current date/time is used.
"""
class IScheduledJob(Interface):
""" A job that will be executed on some external triggering at
a predefined date and time.
"""
startTime = Attribute('Date/time at which the job should be executed.')
params = Attribute('Mapping with key/value pairs to be used by '
'the ``execute()`` method.')
repeat = Attribute('Number of seconds after which the job should be '
'rescheduled. Do not repeat if 0.')
successors = Attribute('Jobs to execute immediately after this '
'one has been finished.')
whenStarted = Attribute('A callable with one argument (the job) that will '
'be called when the job has started.')
whenfinished = Attribute('A callable with two arguments, the job and the '
'result of running the job, that will be called when '
'the job has finished.')
def execute():
""" Execute the job.
Store log information about job execution in a log record.
"""
def reschedule(startTime):
""" Re-schedule the job, setting the date/time the job should be
executed again.
"""
class ILogger(Interface):
""" Ordered collection (list) of log records.
"""
externalLoggers = Attribute('A collection of logger objects '
'to which the logging records should be written.')
def setup():
""" Initialize the logger with the current configuration settings.
"""
def log(data):
""" Record the information given by the ``data`` argument
(a mapping).
"""
class ILogRecord(Interface):
"""
"""
def __str__():
""" Return a string representation suitable for writing on a
log file.
"""
class ICrawlingJob(IScheduledJob):
""" Collects resources.
"""
predefinedMetadata = Attribute('A mapping with metadata to be used '
'for all resources found.')
def collect():
""" Return a deferred that upon callback will provide a
collection of resource objects that should be transferred
to the server.
Use the selection criteria given to filter the resources that
should be collected.
"""
class IResource(Interface):
""" Represents a data object that is collected by a crawler and
will be transferred to the server.
"""
data = Attribute("A string, file, or similar representation of the "
"resource's content")
path = Attribute('A filesystem path or some other information '
'uniquely identifying the resource on the client '
'machine for the current user.')
application = Attribute('The name of the application that provided '
'the resource, e.g. "filesystem" or "mail".')
metadata = Attribute('Information describing this resource; '
'should be an IMetadataSet object.')
class IMetadataSet(Interface):
""" Metadata associated with a resource; a mapping.
"""
def asXML():
""" Return an XML string representing the metadata set.
If this metadata set contains other metadata sets
(nested metadata) this will be converted to XML as well.
"""
def set(key, value):
""" Set a metadata element.
The value may be a string or another metadata set
(nested metadata).
"""
class ITransporter(Interface):
""" Transfers collected resources to the server.
"""
serverURL = Attribute('URL of the server the resources will be '
'transferred to. The URL also determines the '
'transfer protocol, e.g. HTTP or FTP.')
method = Attribute('Transport method, e.g. PUT.')
machineName = Attribute('Name under which the local machine is '
'known to the server.')
userName = Attribute('User name for logging in to the server.')
password = Attribute('Password for logging in to the server.')
def createJob():
""" Return a transport job for this transporter.
"""
def transfer(resource):
""" Transfer the resource (an object providing IResource)
to the server and return a Deferred.
"""
class ITransportJob(IScheduledJob):
""" A job managing the the transfer of a resource to the server.
"""
transporter = Attribute('The transporter object to use for transer.')
class IConfigurator(Interface):
""" Manages (stores and receives) configuration information.
"""
filename = Attribute('The path to a file with configuration parameters.')
def load(p=None, filename=None):
""" Load configuration directly from a string or an open file
(if ``p`` is set) or using the ``filename`` parameter (a path).
If no string and no filename is given the configuration
file is searched in the user's home folder.
If the configuration is loaded from a file using the
``filename`` parameter or from the default location the
path is stored in the ``filename`` attribute.
"""
def save(filename=None):
""" Save configuration settings to the file given, or to the
file from which it was loaded, or to the default location.
"""
# future extensions
class IPackageManager(Interface):
""" Allows to install, update, or remove software packages (plugins,
typically as Python eggs) from a server.
"""
sources = Attribute('A list of URLs that provide software packages. ')
def getInstalledPackages():
""" Return a list of dictionaries, format:
[{'name': name, 'version': version,
'date': date_time_of_installation,}, ...]
"""
def getUpdateCandidates():
""" Return a list of dictionaries with information about updateable
packages.
"""
def installPackage(name, version=None, source=None):
""" Install a package.
If version is not given try to get the most recent one.
If source is not given search the sources attribute for the
first fit.
"""
def updatePackage(name, version=None, source=None):
""" Update a package.
If version is not given try to get the most recent one.
If source is not given search the sources attribute for the
first fit.
"""
def removePackage(name):
""" Remove a package from this agent.
"""

View file

@ -1,76 +0,0 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Log information management.
$Id$
"""
import logging
import sys
import time
from zope.interface import implements
from loops.agent.interfaces import ILogger, ILogRecord
class LogRecord(object):
implements(ILogRecord)
datefmt = '%Y-%m-%dT%H:%S'
def __init__(self, logger, data):
self.logger = logger
self.data = data
self.timeStamp = time.time()
def __str__(self):
msg = [str(time.strftime(self.datefmt, time.localtime(self.timeStamp)))]
for k in sorted(self.data):
msg.append('%s:%s' % (str(k), str(self.data[k])))
return ' '.join(msg)
class Logger(list):
implements(ILogger)
recordFactory = LogRecord
def __init__(self, agent):
self.agent = agent
self.setup()
def setup(self):
self.externalLoggers = []
conf = self.agent.config.logging
if conf.standard:
logger = logging.getLogger()
logger.level = conf.standard
logger.addHandler(logging.StreamHandler(sys.stdout))
self.externalLoggers.append(logger)
def log(self, data):
record = self.recordFactory(self, data)
self.append(record)
for logger in self.externalLoggers:
logger.info(str(record))

View file

@ -1,22 +0,0 @@
"""
Start with ``twistd -noy loops/agent/loops.tac``.
$Id$
"""
from twisted.application import internet, service
from nevow import appserver
from loops.agent.core import Agent
from loops.agent.ui.web import AgentHome
agent = Agent()
conf = agent.config
port = conf.ui.web.port or 10095
application = service.Application('LoopsAgent')
site = appserver.NevowSite(resource=AgentHome(agent))
webServer = internet.TCPServer(port, site)
webServer.setServiceParent(application)

View file

@ -1,118 +0,0 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Job scheduling.
$Id$
"""
from time import time
from twisted.internet import reactor
from twisted.internet.defer import Deferred, succeed
from zope.interface import implements
from loops.agent.interfaces import IScheduler, IScheduledJob
class Scheduler(object):
implements(IScheduler)
def __init__(self, agent):
self.agent = agent
self.queue = {}
@property
def logger(self):
return self.agent.logger()
def schedule(self, job, startTime=None):
if startTime is None:
startTime = int(time())
job.startTime = startTime
job.scheduler = self
while startTime in self.queue:
startTime += 1
self.queue[startTime] = job
reactor.callLater(startTime-int(time()), job.run)
return startTime
def getJobsToExecute(startTime=0):
return [j for j in self.queue.values() if startTime <= j.startTime]
class Job(object):
implements(IScheduledJob)
scheduler = None
whenStarted = lambda self, job: None
whenFinished = lambda self, job, result: None
def __init__(self, **params):
self.startTime = 0
self.params = params
self.successors = []
self.repeat = 0
def execute(self):
""" Must be overridden by subclass.
"""
return succeed('OK')
def reschedule(self, startTime):
return self.scheduler.schedule(self.copy(), startTime)
def run(self):
d = self.execute()
d.addCallback(self.finishRun)
d.addErrback(self.logError)
self.whenStarted(self)
# TODO: logging
def finishRun(self, result):
# remove from queue
del self.scheduler.queue[self.startTime]
# run successors
for job in self.successors:
job.params['result'] = result
#job.run()
self.scheduler.schedule(job)
self.whenFinished(self, result)
# TODO: logging
# reschedule if necessary
if self.repeat:
self.reschedule(int(time() + self.repeat))
def logError(self, error):
print '*** error on running job:', error
def copy(self):
newJob = Job()
newJob.params = self.params
newJob.successors = [s.copy() for s in self.successors]
class Stopper(Job):
def execute(self):
reactor.stop()
return succeed('Agent stopped.')

View file

@ -1,4 +0,0 @@
"""
$Id$
"""

View file

@ -1,53 +0,0 @@
#! /usr/bin/env python
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
A dummy client application for testing purposes.
Run this from above the loops directory with::
python loops/agent/testing/client.py
$Id$
"""
import os
import sys
from time import time
from twisted.internet import reactor
from loops.agent.core import Agent
from loops.agent.crawl.filesystem import CrawlingJob
from loops.agent.transport.base import Transporter
from loops.agent.tests import baseDir
if len(sys.argv) > 1:
cfgName = sys.argv[1]
else:
cfgName = os.path.join(baseDir, 'testing', 'testing.cfg')
cfg = open(cfgName)
agent = Agent(cfg.read())
agent.scheduleJobsFromConfig(stop=True)
print 'Starting reactor.'
reactor.run()
print 'Reactor stopped.'

View file

@ -1,52 +0,0 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
A dummy crawler for testing purposes.
$Id$
"""
from twisted.internet import reactor
from twisted.internet.defer import Deferred
from zope.interface import implements
from loops.agent.interfaces import IResource
from loops.agent.crawl.base import CrawlingJob as BaseCrawlingJob
class CrawlingJob(BaseCrawlingJob):
def collect(self):
deferred = self.deferred = Deferred()
# replace this with the real stuff:
reactor.callLater(0, self.dataAvailable)
return deferred
def dataAvailable(self):
self.deferred.callback([DummyResource()])
class DummyResource(object):
implements(IResource)
data = 'Dummy resource data for testing purposes.'
path = '/dummy/data'
application = 'dummy'
metadata = None

View file

@ -1 +0,0 @@
Data from file1.txt

View file

@ -1 +0,0 @@
Data from file2.txt

View file

@ -1,63 +0,0 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
A dummy webserver for testing the loops.agent HTTP transport.
Run this with::
twistd -noy loops/agent/testing/server.py
$Id$
"""
from twisted.application import service, internet
from twisted.web import http
class RequestHandler(http.Request):
def process(self):
print '***', repr(self.content.read())
if self.method in ('GET', 'POST'):
self.write('<h1>Hello World</h1>')
self.write('<p>dir(self): %s</p>' % dir(self))
self.write('<p>self.path: %s</p>' % self.path)
self.write('<p>self.uri: %s</p>' % self.uri)
self.write('<p>self.args: %s</p>' % self.args)
self.finish()
class HttpServer(http.HTTPChannel):
requestFactory = RequestHandler
class HttpFactory(http.HTTPFactory):
protocol = HttpServer
class HttpService(internet.TCPServer):
def __init__(self):
internet.TCPServer.__init__(self, 8123, HttpFactory())
application = service.Application('Simple Webserver')
HttpService().setServiceParent(application)

View file

@ -1,6 +0,0 @@
crawl[0].type = 'filesystem'
crawl[0].directory = 'loops/agent/testing/data'
crawl[0].pattern = '*.txt'
crawl[0].transport = 'httpput'
crawl[0].repeat = 0
transport.serverURL = 'http://localhost:8123/loops'

View file

@ -1,47 +0,0 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
A dummy transport for testing purposes.
$Id$
"""
from twisted.internet import reactor
from twisted.internet.defer import succeed
from zope.interface import implements
from loops.agent.interfaces import ITransportJob, ITransporter
from loops.agent.transport.base import Transporter as BaseTransporter
class Transporter(BaseTransporter):
def transfer(self, resource):
data = resource.data
if type(data) is file:
text = data.read()
data.close()
else:
text = data
metadata = resource.metadata
if metadata is not None:
print 'Metadata:', metadata
print 'Transferring:', text
return succeed('OK')

View file

@ -1,75 +0,0 @@
#! /usr/bin/env python
#
# Run with ``trial2.4 tests.py`` to execute the twisted unit tests.
# Run with ``python tests.py`` to execute the doctests.
#
# $Id$
import unittest as standard_unittest
import doctest
import os, time
from twisted.internet import reactor
from twisted.internet.defer import Deferred
from twisted.trial import unittest
from loops.agent.schedule import Job
try:
from loops.agent.core import Agent # needs twisted.internet.task.coiterate
ignore = False
except ImportError: # wrong environment, skip all loops.agent tests
print 'Skipping loops.agent'
ignore = True
baseDir = os.path.dirname(__file__)
class Tester(object):
def iterate(self, n=10, delays={}):
for i in range(n):
delay = delays.get(i, 0)
reactor.iterate(delay)
tester = Tester()
class TestJob(Job):
def execute(self, deferred, **kw):
d = super(TestJob, self).execute(**kw)
#print 'executing'
deferred.callback('Done')
return d
class Test(unittest.TestCase):
"Basic tests for the loops.agent package."
def setUp(self):
self.agent = Agent()
def tearDown(self):
pass
def testScheduling(self):
d = Deferred()
job = TestJob()
job.params['deferred'] = d
w = self.agent.scheduler.schedule(job, int(time.time())+1)
return d
def test_suite():
if ignore:
return standard_unittest.TestSuite() # do nothing
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
return standard_unittest.TestSuite((
#standard_unittest.makeSuite(Test),
doctest.DocFileSuite('README.txt', optionflags=flags),
doctest.DocFileSuite('crawl/filesystem.txt', optionflags=flags),
doctest.DocFileSuite('transport/httpput.txt', optionflags=flags),
))
if __name__ == '__main__':
standard_unittest.main(defaultTest='test_suite')

View file

@ -1,4 +0,0 @@
"""
$Id$
"""

View file

@ -1,135 +0,0 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Transporter base classes.
$Id$
"""
from base64 import b64encode
from twisted.internet import reactor
from twisted.internet.defer import Deferred, fail
from twisted.web.client import getPage
from zope.interface import implements
from loops.agent.interfaces import ITransporter, ITransportJob
from loops.agent.schedule import Job
class TransportJob(Job):
implements(ITransportJob)
def __init__(self, transporter):
super(TransportJob, self).__init__()
self.transporter = transporter
self.resources = []
self.deferred = None
def execute(self):
result = self.params.get('result')
if result is None:
return fail('No data available.')
self.resources = result
self.deferred = Deferred()
d = self.transporter.transfer(self.resources.pop())
d.addCallback(self.transferDone).addErrback(self.logError)
return self.deferred
def transferDone(self, result):
self.logTransfer(result)
if self.resources:
d = self.transporter.transfer(self.resources.pop())
d.addCallback(self.transferDone).addErrback(self.logError)
else:
self.deferred.callback('OK')
def logTransfer(self, result, err=None):
#print 'transfer successful; remaining:', len(self.resources)
# TODO: logging
# self.transporter.agent.logger.log(...)
pass
def logError(self, error):
print '*** error on transfer', self.transporter.serverURL, error
self.deferred.errback(error)
class Transporter(object):
implements(ITransporter)
jobFactory = TransportJob
serverURL = 'http://localhost:8080'
method = 'PUT'
machineName = 'unknown'
userName = 'nobody'
password = 'secret'
def __init__(self, agent, **params):
self.agent = agent
for k, v in params.items():
setattr(self, k ,v)
self.deferred = None
def createJob(self):
return self.jobFactory(self)
def transfer(self, resource):
data = resource.data
if type(data) is file:
text = data.read()
data.close()
else:
text = data
path = resource.path
app = resource.application
metadata = resource.metadata
auth = b64encode(self.userName + ':' + self.password)
headers = {'Authorization': 'Basic ' + auth}
url = self.makePath('.data', app, path)
d = getPage(url, method=self.method, headers=headers, postdata=text)
if metadata is None:
d.addCallback(self.finished)
else:
d.addCallback(self.transferMeta, app, path, headers, metadata.asXML())
self.deferred = Deferred()
return self.deferred
def transferMeta(self, result, app, path, headers, text):
url = self.makePath('.meta', app, path)
d = getPage(url, method=self.method, headers=headers, postdata=text)
d.addCallback(self.finished)
def finished(self, result):
self.deferred.callback(result)
def makePath(self, infoType, app, path, extension=None):
if path.startswith('/'):
path = path[1:]
url = self.serverURL
if url.endswith('/'):
url = url[:-1]
fullPath = '/'.join((url, infoType,
self.machineName, self.userName, app, path))
if extension:
fullPath += '.' + extension
return fullPath

View file

@ -1,15 +0,0 @@
======================================================
loops.agent.transport.httpput - The HTTP PUT Transport
======================================================
($Id$)
>>> from time import time
>>> from loops.agent.core import Agent
>>> from loops.agent.transport.base import Transporter
>>> agent = Agent()
>>> transporter = Transporter(agent)
>>> job = transporter.createJob()

View file

@ -1,26 +0,0 @@
#-------------------------------------------------
# StartUpAgentUI.tac.py
# start the twisted webserver and initialize the
# nevow framework/application
# author: Juergen Menzinger
# version: 01.alpha
# start with twistd.py -noy StartUpAgentUI.tac.py
# requires AgentStart.html
# AgentJobView.html
# AgentJobViewDetail.html
# AgentOutlookMailView.html
#-------------------------------------------------
from twisted.application import internet
from twisted.application import service
from nevow import appserver
import web
import sys, os, socket
application = service.Application('loops agent')
site = appserver.NevowSite(web.AgentHome())
webServer = internet.TCPServer(8080, site)
webServer.setServiceParent(application)

View file

@ -1,4 +0,0 @@
"""
$Id$
"""

View file

@ -1,4 +0,0 @@
FileSysCont_1234;Running;daily;*.doc;C:\Documents;size<100kByte;transfer(http:loops.sampleserver.de)
MailCrawl_2121;Running;daily;Subject:Update;Outlook;Account=Account_Root;transfer(http:loops.sampleserver.de)
MailCrawl_4040;Paused;20s;Subject:Update;Outlook;Account=Account_Root;transfer(http:loops.sampleserver.de)
FileSysCont_8797;Stopped;10m;*.jpg;C:\Tmp;size<100kByte;transfer(http:loops.sampleserver.de)

View file

@ -1,99 +0,0 @@
/*
$Id$
based on http://www.tjkdesign.com/articles/liquid/4.asp
*/
body {
min-width:640px;
margin: 0;
padding: 0;
font: 9pt Verdana, Tahoma, Arial, Helvetica, sans-serif;
background-color: white;
color: #000040;
}
#global,#menu,#sub-section,#footer {
overflow: hidden;
display: inline-block;
}
#content {
display: inline-block;
}
#global,#footer {width:100%}
#menu,#content,#sub-section {float:left}
#menu {width:20%}
#content {width:62%}
#sub-section {width:17%}
#footer {clear:left}
/* more general stuff */
.top image {
margin-top: -1px;
}
div.box {
margin: 12px 12px 8px 10px;
border: 1px solid #ccc;
border-bottom: none;
}
div.box h4 {
font: 110% Verdana, Tahoma, Arial, Helvetica, sans-serif;
color: #000040;
border: none;
border-bottom: 1px solid #ccc;
padding: 4px;
padding-top: 1px;
padding-bottom: 3px;
background-color: #ddd;
height: auto;
}
table.listing {
margin: 1px;
margin-top: 6px;
}
table.listing th {
font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif;
color: #000040;
}
.footer {
text-align: center;
border-top: 1px solid #ccc;
border-bottom: none;
margin-top: 12px;
padding-top: 6px;
}
.itemViews {
border-bottom-width: 2px;
}
.button {
margin: 1em 0 1em 0;
}
.button a {
padding: 2px 4px 2px 4px;
background-color: #e8e8e8;
text-decoration: None;
color: Black;
border-width: 2px;
border-style: solid;
border-color: #f4f4f4 #989898 #989898 #f4f4f4;
}
pre {
background-color: #f4f4f4;
}
#footer { border-bottom: none; }

View file

@ -1,28 +0,0 @@
/*
$Id$
cyberconcepts specialties
*/
body {
color: #242424;
}
a {
text-decoration: none;
color: #344080;
background-color: transparent;
}
.top {
border-bottom: 1px solid #d0d0d0;
margin-bottom: 2px;
/*background-image: url('bg_cyberview.gif')
height: 75px;*/
}
table.listing tr:hover {
background-color: #FEFE82;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 580 B

View file

@ -1,138 +0,0 @@
/*
$Id$
settings specific for view / node objects
*/
a[href]:hover {
text-decoration: none;
color: #803000;
}
pre {
overflow: scroll;
max-height: 35em;
}
.box div.body div.even {
background-color: #f4f4f4;
}
.box {
margin: 12px;
}
#body {
margin-left: 5px;
}
/*.content-1 h1 { */
h1 {
font-size: 160%;
font-weight: bold;
}
.content-2 h1, h2 {
font-size: 140%;
}
.content-3 h1, .content-2 h2, h3 {
font-size: 130%;
font-weight: bold;
}
.content-4 h1, .content-3 h2, .content-2 h3, .content-1 h4 {
font-size: 120%;
}
.content-5 h1, .content-4 h2, .content-3 h3, content-2 h4 {
font-size: 100%;
border: none;
}
.subcolumn {
display: inline;
float: left;
}
.box {
margin: 5px;
padding: 6px;
padding-top: 0;
}
.box h1, .box h2, .box h3 {
border-bottom: None;
}
div.menu-1, div.menu-2 {
border-top: 1px solid #eeeeee;
font-weight: bold;
}
.box div.body div.menu-3 {
border-top: none;
padding-left: 1.5em;
}
.box div.body div.menu-4 {
padding-left: 3em;
font-size: 90%
}
.flow-left {
float: left;
}
.flow-right {
float: right;
}
div.image {
margin-top: 10px;
margin-right: 5px;
}
/* search stuff */
.searchForm input.button, input.submit {
padding: 2px;
}
.searchForm input.submit {
font-weight: bold;
}
/* dojo stuff */
/*.dojoComboBox {
width: 200px;
}*/
.dojoDialog {
background: #eee;
border: 1px solid #999;
-moz-border-radius: 5px;
padding: 4px;
}
.dojoDialog th {
font-size: 120%;
padding: 0 5px 8px 5px;
}
.dojoDialog .headline {
font-weight: bold;
}
.dojoDialog input.text {
width: 100%;
margin-right: 10px;
}
.dojoDialog input.submit {
font-weight: bold;
}

View file

@ -1,126 +0,0 @@
/* $Id: loops.js 1667 2007-03-26 10:43:15Z helmutm $ */
function openEditWindow(url) {
zmi = window.open(url, 'zmi');
zmi.focus();
return false;
}
function focusOpener() {
if (typeof(opener) != 'undefined' && opener != null) {
opener.location.reload();
opener.focus();
}
}
function replaceFieldsNode(targetId, typeId, url) {
token = dojo.byId(typeId).value;
uri = url + '?form.type=' + token;
dojo.io.updateNode(targetId, uri);
}
function submitReplacing(targetId, formId, actionUrl) {
dojo.io.updateNode(targetId, {
url: actionUrl,
formNode: dojo.byId(formId),
method: 'post'
});
return false;
}
function inlineEdit(id, saveUrl) {
var iconNode = dojo.byId('inlineedit_icon');
iconNode.style.visibility = 'hidden';
editor = dojo.widget.createWidget('Editor',
{items: ['save', '|', 'formatblock', '|',
'insertunorderedlist', 'insertorderedlist', '|',
'bold', 'italic', '|', 'createLink', 'insertimage'],
saveUrl: saveUrl,
//closeOnSave: true,
htmlEditing: true
//onClose: function() {
/* onSave: function() {
this.disableToolbar(true);
iconNode.style.visibility = 'visible';
//window.location.reload();
}*/
}, dojo.byId(id));
editor._save = function (e) {
if (!this._richText.isClosed) {
if (this.saveUrl.length) {
var content = {};
this._richText.contentFilters = [];
content[this.saveArgName] = this.getHtml();
content['version'] = 'this';
dojo.io.bind({method:this.saveMethod,
url:this.saveUrl,
content:content,
handle:function(type, data, ti, kwargs) {
location.reload(false);
}
}); //alert('save');
} else {
dojo.debug("please set a saveUrl for the editor");
}
if (this.closeOnSave) {
this._richText.close(e.getName().toLowerCase() == "save");
}
}
}
return false;
}
function setConceptTypeForComboBox(typeId, cbId) {
var t = dojo.byId(typeId).value;
var cb = dojo.widget.manager.getWidgetById(cbId)
var dp = cb.dataProvider;
var baseUrl = dp.searchUrl.split('&')[0];
var newUrl = baseUrl + '&searchType=' + t;
dp.searchUrl = newUrl;
cb.setValue('');
}
var dialogs = {}
function objectDialog(dlgName, url) {
dojo.require('dojo.widget.Dialog');
dojo.require('dojo.widget.ComboBox');
dlg = dialogs[dlgName];
if (!dlg) {
//dlg = dojo.widget.fromScript('Dialog',
dlg = dojo.widget.createWidget('Dialog',
{bgColor: 'white', bgOpacity: 0.5, toggle: 'fade', toggleDuration: 250,
executeScripts: true,
href: url
}, dojo.byId('dialog.' + dlgName));
dialogs[dlgName] = dlg;
}
dlg.show();
}
function addConceptAssignment() {
dojo.require('dojo.html')
node = dojo.byId('form.assignments');
els = document.forms[0].elements;
for (var i=0; i<els.length; i++) { //getElementsByName does not work here in IE
el = els[i];
if (el.name == 'concept.search.text_selected') {
cToken = el.value;
} else if (el.name == 'concept.search.text') {
title = el.value;
}
}
if (cToken.length == 0) {
alert('Please select a concept!');
return false;
}
pToken = dojo.byId('concept.search.predicate').value;
token = cToken + ':' + pToken;
var td = document.createElement('td');
td.colSpan = 5;
td.innerHTML = '<input type="checkbox" name="form.assignments.selected:list" value="' + token + '" checked><span>' + title + '</span>';
var tr = document.createElement('tr');
tr.appendChild(td);
node.appendChild(tr);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,13 +0,0 @@
/*
$Id$
*/
.top, #header, #menu, #sub-section, #footer {
display: none;
}
#content {
width: 100%;
color: Black;
}

View file

@ -1,662 +0,0 @@
/*
** Zope3 style sheet for CSS2-capable browsers.
** For future skin see zope.app.boston.
*/
/*
* { border: 1px dotted red }
*/
/* Basic Elements */
body {
font: 85% Helvetica, Arial, sans-serif;
background: White;
color: Black;
margin: 0;
padding: 0;
/* These work in IE only, changes the look of the scrollbar + textareas */
scrollbar-base-color: White;
scrollbar-highlight-color: White;
scrollbar-track-color: #F8F8F8;
scrollbar-darkshadow-color: #F8F8F8;
scrollbar-3dlight-color: #369;
scrollbar-shadow-color: #369;
scrollbar-arrow-color: Black;
}
table {
border-collapse: collapse;
font-size: 100%;
}
a {
text-decoration: none;
color: #369;
background-color: transparent;
}
a[href]:active {
text-decoration: underline;
}
img {
border: none;
vertical-align: middle;
}
p {
margin: 0.5em 0em 1em 0em;
line-height: 1.5em;
}
p a:visited {
color: Purple;
background-color: transparent;
}
p a:active {
color: Red;
background-color: transparent;
}
p img {
border: 0;
margin: 0;
}
hr {
clear: both;
height: 1px;
color: #369;
background-color: transparent;
}
h1, h2, h3, h4, h5, h6 {
color: Black;
clear: left;
font: 100% bold Verdana, Helvetica, Arial, sans-serif;
margin: 0;
padding-top: 0.5em;
border-bottom: 1px solid #369;
}
h1 {
font-size: 160%;
}
h2 {
font-size: 150%;
}
h3 {
font-size: 140%;
}
h4 {
font-size: 120%;
}
h5 {
font-size: 100%;
}
h6 {
font-size: 80%;
}
ul {
line-height: 1.5em;
/* list-style-image: url("bullet.gif"); */
margin-left: 2em;
padding:0;
}
ol {
line-height: 1.5em;
margin-left: 2em;
padding:0;
}
dl {
}
dt {
font-weight: bold;
}
dd {
line-height: 1.5em;
margin-bottom: 1em;
}
fieldset {
border: 1px solid #A0A0A0;
/*
margin: 2em 0em 1em 0em;
padding: 1em 0em;
*/
margin: 0em 0em 2em 0em;
padding: 0 1em 1em 1em;
}
legend {
background: White;
padding: 0.5em;
}
form {
border: none;
margin: 0;
}
textarea {
color: Black;
width: 88%;
padding: 0.1em;
}
input {
font: normal 100% Verdana, Helvetica, Arial, sans-serif;
color: Black;
vertical-align: middle;
margin-bottom: 1px; /* IE bug fix */
padding: 0.1em;
}
select {
font: normal 100% Verdana, Helvetica, Arial, sans-serif;
vertical-align: top;
}
abbr, acronym, .explain {
border-bottom: 1px dotted Black;
color: Black;
background-color: transparent;
cursor: help;
}
q {
font-family: Times, "Times New Roman", serif;
font-style: italic;
font-size: 120%;
}
blockquote {
font-family: Times, "Times New Roman", serif;
font-style: italic;
font-size: 120%;
}
code {
font-size: 120%;
color: Black;
background-color: #CCCCCC;
}
pre {
font-size: 120%;
padding: 1em;
border: 1px solid #A0A0A0;
color: Black;
background-color: #CCCCCC;
}
.netscape4 {
display: none;
}
/* layout table
*/
#layout {
width: 100%;
table-layout: auto;
font-size: 100%;
border-collapse: collapse;
padding: 0px;
margin: 0px;
}
#layout td {
vertical-align: top;
}
#layout td.global {
width: 100%;
}
#layout td.navigators {
width: 200px;
}
#layout td.workspace {
}
/* Styles for xmltree
*/
#navtreecontents {
padding-right: 35px;
}
#navtreecontents a {
cursor: pointer;
height: 20px;
}
#navtreecontents loading {
display: block;
padding-left: 31px;
height: 18px;
}
#navtreecontents expand {
background-repeat: no-repeat;
padding-left: 14px;
display: inline;
cursor: pointer;
}
#navtreecontents icon {
background-repeat: no-repeat;
padding-left: 20px;
display: inline;
cursor: auto;
}
#navtreecontents collection {
display: block;
margin-left: 10px;
/* border: red solid 1pt; */
height: auto;
}
/* Structural elements
*/
#top {
border-bottom: 0.1em solid black;
}
#top #userDetails {
float:right;
margin-top: 1.2em;
padding-right: 0.5em;
}
div#action {
height: 24px;
width: 100%;
background-color: #336699;
}
div#action ul {
line-height: 24px;
color: #FFF;
white-space: nowrap;
float: right;
margin: 0px;
padding: 0px 5px 0px 0px;
}
div#action li {
list-style-type: none;
display: inline;
}
div#action li a {
color: #FFF;
text-decoration: none;
border-left: 1px dashed white;
padding: 0px 5px;
}
div#action li a:link {
color: #FFF;
}
div#action li a:hover {
color: black;
background-color: white;
}
#breadcrumbs {
margin: 0;
padding: 5px 5px 5px 5px;
}
#navigation {
width: 200px;
vertical-align: top;
padding: 0px;
margin: 0px;
}
#navigators {
padding: 10px 20px 0px 5px;
}
/* slot boxes
*/
div.box {
background: #CCCCCC;
border-right: 1px solid #CCCCCC;
border-left: 1px solid #CCCCCC;
margin-bottom: 10px;
padding: 0px;
}
div.box h4 {
background: #CCCCCC;
border: 1px solid #CCCCCC;
border-style: solid solid none solid;
color: #808080;
padding: 0px 5px;
display: block;
height: 22px;
}
.box div.body {
background: white;
border-bottom: 1px solid #CCCCCC;
}
.box div.body div {
color: #777777;
padding: 2px 0px 5px 5px;
margin: 0px;
}
.box div.treebody {
background: white;
border-bottom: 1px solid #CCCCCC;
}
.box div.treebody table {
margin: 2px;
}
.box div.body div.tip {
color: #B30000;
padding: 2px 0px 5px 5px;
}
.box div.body div.even {
background: #EBEBE2;
padding: 4px;
}
.box div.body div.odd {
padding: 4px;
}
.box .content {
padding: 0.5em;
}
.box h1,
.box h2,
.box h3,
.box h4 {
margin: 0;
}
#content {
}
#context_information {
padding-top: 1em;
width: 15%;
float: left;
padding-left: 0.5em;
}
#workspace {
width: 100%;
}
#helpers {
}
#inspectors {
}
#footer {
border-bottom: 1px solid black;
float: left;
clear: both;
}
input.textType {
width: 88%; /* Same as textarea */
}
input.editcheck {
float:left;
position:relative;
top:1em;
}
div.row {
padding-top: 1em;
}
/*
div.label {
#clear: both;
padding-top: 10px;
}
*/
/* div.row div.field doesn't appear to be selecting. div.row div
is a workaround */
/* This seems to work in Firefox 1.0 and IE6. */
div.row div.field {
clear: left;
padding-top: 1px;
}
div.row div.label {
background: #369;
color: #fff;
padding: 0.1em 0.5em 0.1em 0.5em; /* Same as .itemViews */
border: 1px solid #369; /* Same as .itemViews */
margin: 0;
float: left;
clear: both;
}
div.row span.error {
background: red;
color: white;
padding: 0.1em 0.5em 0.1em 0.5em; /* Same as .itemViews */
border: 1px solid red; /* Same as .itemViews */
margin: 0;
float: left;
clear: both;
}
/*
div.row div.error:before {
content: "\2190 ";
}
*/
#metadata .label {
font-size: 80%;
}
.itemViews {
background: transparent;
border-collapse: collapse;
border-bottom: 1px solid #369;
padding-top: 1px;
padding-bottom: 1px;
padding-left: 1em;
margin-top: 0.8em;
white-space: nowrap;
}
.itemViews a {
background: transparent;
border: 1px solid #369;
color: Black;
font-weight: normal;
margin-right: 0.5em;
padding: 0.1em 0.5em 0.1em 0.5em;
}
.itemViews a.selected {
background: #369;
border-bottom: #369 1px solid;
color: White;
font-weight: normal;
}
.itemViews a:hover {
background-color: #369;
color: White;
}
#viewspace {
border-collapse: collapse;
margin: 0;
}
table.listingdescription, table.listing {
/* The default table for document listings. Contains name, document types, modification times etc in a file-browser-like fashion */
border-collapse: collapse;
border-left: 1px solid #CCCCCC;
border-bottom: 1px solid #CCCCCC;
margin: 1em 0em 1em 0em;
/* clear: both; */
}
table.listingdescription {
width: 100%;
}
table.listingdescription th, table.listing th {
background: #CCCCCC;
border-top: 1px solid #CCCCCC;
border-bottom: 1px solid #CCCCCC;
border-right: 1px solid #CCCCCC;
color: #808080;
font-weight: normal;
padding: 0em 1em 0em 1em;
white-space: nowrap;
}
table.listingdescription td.top, table.listing td.top {
border-left: 1px solid White;
border-top: 1px solid White ! important;
border-right: 1px solid White ! important;
text-align: right ! important;
padding: 0em 0em 1em 0em;
/* insane IE row bug workaround */
position: relative;
left: -1px;
top: -1px;
}
table.listingdescription tr.odd, table.listing tr.odd {
/*every second line should be shaded */
background: White;
}
table.listingdescription tr.even, table.listing tr.even {
background: #F8F8F8;
}
table.listing td {
border-right: 1px solid #CCCCCC;
padding: 0em 0.3em;
text-align: left;
white-space: nowrap;
}
table.listingdescription img, table.listing img {
vertical-align: middle;
}
table.listingdescription td {
border-right: 1px solid #CCCCCC;
padding: 5px;
text-align: left;
}
/*colorize the matrix table used in grant.html*/
table.matrix td.default {
background: green;
}
table.matrix td.changed {
background: red;
}
div.spacer {
clear: both;
}
.registrationSummary {
margin-left: 2em;
margin-bottom: 1em;
}
.registrationSummary .usageSummary {
font-weight: bold;
}
.registrationSummary .modificationLink {
display: block;
}
div.message {
background: #FFCE7B;
border: 1px solid #FFA500;
color: Black;
font: bold 80% Verdana, Helvetica, Arial, sans-serif;
margin: 2em 0em 1em 0em;
padding: 0.5em 1em;
vertical-align: middle;
}
div.message a {
color: Black;
}
/* Style for page error divs. Use this for displaying errors for a
page as a whole.
*/
div.page_error {
background: #FFCE7B;
font: bold 80% Verdana, Helvetica, Arial, sans-serif;
padding: 0.5em 1em;
vertical-align: middle;
}
div.bug {
background: #FFCE7B;
border: 1px solid #FFA500;
color: Black;
font: bold 80% Verdana, Helvetica, Arial, sans-serif;
margin: 2em 1em 1em 0em;
padding: 0.5em 1em;
vertical-align: middle;
}

View file

@ -1,70 +0,0 @@
<!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" xml:lang="en" lang="en"> //-->
<html xmlns:nevow="http://nevow.com/ns/nevow/0.1">
<!-- StartPage for loops.agent UI Version: 0.1 //-->
<nevow:invisible nevow:render="header_fragment" />
<body>
<div class="body">
<div nevow:render="top_fragment" >
</div>
<div id="menu">
<div nevow:render="navigation_fragment">
</div>
<br/>
<br/>
<div class="box">
<h4>User Mode</h4>
<div class="body">
<b>Current Mode: </b><p nevow:render="getActiveUserMode"/>
<p><div align="center"><a href="changeUserMode"><b>[Switch Mode]</b></a></div></p>
</div>
</div>
</div>
<div id="content">
<div></div>
<div></div>
<div class="content-1" id="2.body" ondblclick="">
<div class="line-block">
<div class="line"><br /></div>
</div>
<div class="section">
<h3><a id="agent-ui-startpage" name="agent-ui-startpage">Startpage</a></h3>
<ul class="simple">
<li>This page contains some information about the loops agent UI. On the left side you can find the
navigation panel where you can choose between a general job overview to be displayed, add a job or change the
logging options. Also below this navigation menue you will find a button which switches the User mode between
<i>Professional</i> and <i>Simple Mode</i>. This button is to be found on this start page only.</li>
</ul>
</div>
<div class="section">
<h3><a id="agent-version" name="agent-version">Version</a></h3>
<ul class="simple">
<li>Agent: <div nevow:render="getAgentVersion"/></li>
</ul>
</div>
</div>
</div>
<div id="sub-section" define-macro="sub-section">
</div>
<div nevow:render="footer_fragment">
</div>
</div>
</body>
</html>

View file

@ -1,228 +0,0 @@
<!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" xml:lang="en" lang="en"> //-->
<html xmlns:nevow="http://nevow.com/ns/nevow/0.1">
<!-- Outlook Mails Page for loops.agent UI Version: 0.1 //-->
<nevow:invisible nevow:render="header_fragment" />
<body>
<div class="body">
<div nevow:render="top_fragment">
</div>
<div id="menu">
<div nevow:render="navigation_fragment">
</div>
<br/>
<br/>
<div class="box">
<h4>User Mode</h4>
<div class="body">
<b>Current Mode: </b><p nevow:render="getActiveUserMode"/>
</div>
</div>
</div>
<div id="content">
<div></div>
<div></div>
<div class="content-1" id="2.body" ondblclick="">
<div class="line-block">
<div class="line"><br /></div>
</div>
<div class="section">
<h3><a id="agent-ui-startpage" name="agent-ui-startpage">Agent: Create Filesystem Crawler Job</a></h3>
<ul class="simple">
<li>Configuration page for Filesystem Crawler Jobs</li>
</ul>
</div>
<div class="section">
<h3><a id="form-overview" name="form-overview">Overview</a></h3>
<ul class="simple">
<li><b>File Collection</b><div nevow:render="data" nevow:data="displayViewForm"/></li>
</ul>
<div align="center">
<table border="0" cellpadding="10">
<p nevow:render="displayFiles">
<tr nevow:pattern="CollectedFiles" nevow:render="data"/>
</p>
</table>
<form name="FileCrawlForm" action="submitFilesystemCrawlJob" method="POST">
<fieldset>
<legend>
Filesystem Crawl Settings
</legend>
<fieldset>
<legend>Directories to crawl</legend>
<table align="center">
<tr>
<td>
<label for="lblRecursiveDirs">Directories to crawl <b>recursively</b></label>
<p>
<i>please use ';' as delimiters</i>
</p>
<p>
<i>you can also use patterns like regular expressions </i>
</p>
</td>
<td>
<textarea name="rec_directories" id="lblRecursiveDirs" rows="10" cols="40"></textarea>
</td>
</tr>
<tr>
<td>
<label for="lblDirs">Directories to crawl non-recursively</label>
<p>
<i>please use ';' as delimiters</i>
</p>
<p>
<i>you can also use patterns like regular expressions </i>
</p>
</td>
<td>
<textarea name="rec_directories" id="lblDirs" rows="10" cols="40"></textarea>
</td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>
Filter criteria patterns
</legend>
<table align="center">
<tr>
<td>
<label for="lblApplyFileSize">Apply size criteria : </label>
</td>
<td>
<input id="lblApplyFileSize" name="applyFileSize" type="checkbox"/>
</td>
</tr>
<tr>
<td>
<label for="lblFileSizeLimit">Collect files that are : </label>
</td>
<td>
<select id="lblFileSizeLimit" name="selectFileSizeLimit">
<option>greater</option>
<option>less</option>
<option>equal</option>
</select>
</td>
</tr>
<tr>
<td>
<label for="lblFileSize">than (kByte): </label>
</td>
<td>
<input id="lblFileSize" name="fileSize"
type="text" size="12" maxlength="20" />
</td>
</tr>
<tr>
<td>
<label for="lblMaximumSize">Maximum file size: </label>
<p>
<i>no size means that transferred files might be very large!</i>
</p>
</td>
<td>
<input id="lblMaximumSize" name="maximumSize"
type="text" size="12" maxlength="20" />
</td>
</tr>
<tr>
<td>
<label for="lblApplyDateCriteria">Apply date criteria: </label>
</td>
<td>
<input id="lblApplyDateCriteria" name="applyDateCriteria" type="checkbox"/>
</td>
</tr>
<tr>
<td>
<label for="lblDateCriteria">Collect files that are: </label>
</td>
<td>
<select id="lblDateCriteria" name="selectDateCriteria">
<option>created</option>
<option>modified</option>
<option>accessed</option>
</select>
</td>
</tr>
<tr>
<td>
<label for="lblTimestampCompare"></label>
</td>
<td>
<select id="lblTimestampCompare" name="selectTimeStampCompare">
<option>before</option>
<option>after</option>
<option>exactly on</option>
</select>
</td>
</tr>
<tr>
<td>
<label for="lblTimeStamp">Collect files that are : </label>
</td>
<td>
<input id="lblTimeStamp" name="selectDateCriteria" type="text" size="20"/>
</td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>
Job Interval
</legend>
<table align="center">
<tr>
<td>
<label for="lblmailCrawlIntervaloneTime">One Time: </label>
</td>
<td>
<input type="radio" id="lblmailCrawlIntervaloneTime" name="mailCrawlInterval"
value="oneTime" checked="checked" />
</td>
</tr>
<tr>
<td>
<label for="lblmailCrawlIntervalScheduler">Use Scheduler: </label>
</td>
<td>
<input type="radio" id="lblmailCrawlIntervalScheduler" name="mailCrawlInterval"
value="Scheduler" />
</td>
</tr>
</table>
</fieldset>
<input type="submit" name="startCrawlJob" value="Save and Start" />
</fieldset>
</form>
<div nevow:render="systemMessage"/>
</div>
</div>
</div>
</div>
<div id="sub-section" define-macro="sub-section">
</div>
<div nevow:render="footer_fragment">
</div>
</div>
</body>
</html>

View file

@ -1,7 +0,0 @@
<div id="footer" class="footer" define-macro="footer" content="text/html; charset=UTF-8">
&#169; Copyright 2007, cyberconcepts IT-Consulting Dr. Helmut Merz
(<a href="http://loops.cy55.de/impressum">Impressum</a>)<br />
Powered by <b><a href="http://www.python.org">Python</a></b> &#183;
<b><a href="http://wiki.zope.org/zope3">Zope 3</a></b> &#183;
<b><a href="http://loops.cy55.de/projekte/loops">loops</a></b>.
</div>

View file

@ -1,22 +0,0 @@
<head>
<title>loops Agent</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css" media="all">@import url("/resources/zope3_tablelayout.css");</style>
<style type="text/css" media="screen">@import url("/resources/base.css");</style>
<style type="text/css" media="print">@import url("/resources/print.css");</style>
<style type="text/css" media="all">@import url("/resources/loops.css");</style>
<style type="text/css" media="all">@import url("/resources/custom.css");</style>
<script type="text/javascript" src="/resources/loops.js">
</script>
<link rel="icon" type="image/png" href="/resources/favicon.png" />
<base href="AgentStart.html"/>
</head>

View file

@ -1,79 +0,0 @@
<!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" xml:lang="en" lang="en"> //-->
<html xmlns:nevow="http://nevow.com/ns/nevow/0.1">
<!-- Detailed Job View Page for loops.agent UI Version: 0.1 //-->
<nevow:invisible nevow:render="header_fragment" />
<body>
<div class="body">
<div nevow:render="top_fragment">
</div>
<div id="menu">
<div nevow:render="navigation_fragment">
</div>
<br/>
<br/>
<div class="box">
<h4>User Mode</h4>
<div class="body">
<b>Current Mode: </b><p nevow:render="getActiveUserMode"/>
</div>
</div>
</div>
<div id="content">
<div></div>
<div></div>
<div class="content-1" id="2.body" ondblclick="">
<div class="line-block">
<div class="line"><br /></div>
</div>
<div class="section">
<h3><a id="agent-ui-startpage" name="agent-ui-startpage">Agent Job overview</a></h3>
<ul class="simple">
<li>Here you will get an overview of the current jobs registered in the agent system</li>
</ul>
</div>
<div class="section">
<h3><a id="form-overview" name="form-overview">Overview</a></h3>
<ul class="simple">
<li><b>Job details</b><div nevow:render="data" nevow:data="displayViewForm"/></li>
<table class="listing">
<thead>
<tr>
<th>
Attribute
</th>
<th>
Value
</th>
</tr>
</thead>
<tbody>
<p nevow:render="displayJobDetails">
</p>
</tbody>
</table>
<br/>
<a href="joboverview" alt="back to Job Overview Page"><b>[back to Job Overview]</b></a>
</ul>
</div>
</div>
</div>
<div id="sub-section" define-macro="sub-section">
</div>
<div nevow:render="footer_fragment">
</div>
</div>
</body>
</html>

View file

@ -1,89 +0,0 @@
<!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" xml:lang="en" lang="en"> //-->
<html xmlns:nevow="http://nevow.com/ns/nevow/0.1">
<!-- Job Overview Page for loops.agent UI Version: 0.1 //-->
<nevow:invisible nevow:render="header_fragment" />
<body>
<div class="body">
<div nevow:render="top_fragment">
</div>
<div id="menu">
<div nevow:render="navigation_fragment">
</div>
<br/>
<br/>
<div class="box">
<h4>User Mode</h4>
<div class="body">
<b>Current Mode: </b><p nevow:render="getActiveUserMode"/>
</div>
</div>
</div>
<div id="content">
<div></div>
<div></div>
<div class="content-1" id="2.body" ondblclick="">
<div class="line-block">
<div class="line"><br /></div>
</div>
<div class="section">
<h3><a id="agent-ui-startpage" name="agent-ui-startpage">Agent Job overview</a></h3>
<ul class="simple">
<li>Here you will get an overview of the current jobs registered in the agent system</li>
</ul>
</div>
<div class="section">
<h3><a id="form-overview" name="form-overview">Overview</a></h3>
<ul class="simple">
<li><b>Current jobs </b><div nevow:render="data" nevow:data="displayViewForm"/></li>
<table class="listing">
<thead>
<tr>
<th>
PID
</th>
<th>
State
</th>
<th>
Interval
</th>
<th>
Search Criteria
</th>
<th>
Job Scope
</th>
</tr>
</thead>
<tbody>
<p nevow:render="fillJobTable">
</p>
</tbody>
</table>
</ul>
</div>
</div>
</div>
<div id="sub-section" define-macro="sub-section">
</div>
<div nevow:render="footer_fragment">
</div>
</div>
</body>
</html>

View file

@ -1,87 +0,0 @@
<!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" xml:lang="en" lang="en"> //-->
<html xmlns:nevow="http://nevow.com/ns/nevow/0.1">
<!-- Outlook Mails Page for loops.agent UI Version: 0.1 //-->
<nevow:invisible nevow:render="header_fragment" />
<body>
<div class="body">
<div nevow:render="top_fragment">
</div>
<div id="menu">
<div nevow:render="navigation_fragment">
</div>
<br/>
<br/>
<div class="box">
<h4>User Mode</h4>
<div class="body">
<b>Current Mode: </b><p nevow:render="getActiveUserMode"/>
</div>
</div>
</div>
<div id="content">
<div></div>
<div></div>
<div class="content-1" id="2.body" ondblclick="">
<div class="line-block">
<div class="line"><br /></div>
</div>
<div class="section">
<h3><a id="agent-ui-startpage" name="agent-ui-startpage">Agent: collected Outlook Mails</a></h3>
<ul class="simple">
<li>All currently available Outlook Mails collected by the loops agent</li>
</ul>
</div>
<div class="section">
<h3><a id="form-overview" name="form-overview">Overview</a></h3>
<ul class="simple">
<li><b>Mail in Detail</b><div nevow:render="data" nevow:data="displayViewForm"/></li>
</ul>
<div nevow:render="systemMessage"/>
<div align="center">
<table class="listing" style="width:200px">
<thead>
<tr>
<th>
Field
</th>
<th>
Value
</th>
</tr>
</thead>
<tbody>
<p nevow:render="displayOutlookMail">
</p>
</tbody>
</table>
</div>
<br/>
<a href="viewRessources" alt="back to Ressources overview"><b>[back to Ressources overview]</b></a>
</div>
</div>
</div>
<div id="sub-section" define-macro="sub-section">
</div>
<div nevow:render="footer_fragment">
</div>
</div>
</body>
</html>

View file

@ -1,197 +0,0 @@
<!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" xml:lang="en" lang="en"> //-->
<html xmlns:nevow="http://nevow.com/ns/nevow/0.1">
<!-- Outlook Mails Page for loops.agent UI Version: 0.1 //-->
<nevow:invisible nevow:render="header_fragment" />
<body>
<div class="body">
<div nevow:render="top_fragment">
</div>
<div id="menu">
<div nevow:render="navigation_fragment">
</div>
<br/>
<br/>
<div class="box">
<h4>User Mode</h4>
<div class="body">
<b>Current Mode: </b><p nevow:render="getActiveUserMode"/>
</div>
</div>
</div>
<div id="content">
<div></div>
<div></div>
<div class="content-1" id="2.body" ondblclick="">
<div class="line-block">
<div class="line"><br /></div>
</div>
<div class="section">
<h3><a id="agent-ui-startpage" name="agent-ui-startpage">Agent: Create Mail Crawl Job</a></h3>
<ul class="simple">
<li>Configuration page for Outlook Mail Crawler jobs.</li>
</ul>
</div>
<div class="section">
<h3><a id="form-overview" name="form-overview">Overview</a></h3>
<ul class="simple">
<li><b>Mail Collection</b><div nevow:render="data" nevow:data="displayViewForm"/></li>
</ul>
<div align="center">
<table border="0" cellpadding="10">
<p nevow:render="displayOutlookMails">
<tr nevow:pattern="OutlookMails" nevow:render="data"/>
</p>
</table>
<form name="OutlookCrawlForm" action="submitOutlookCrawlJob" method="POST">
<fieldset>
<legend>
Mail Crawl Settings
</legend>
<fieldset>
<legend>Folders to crawl</legend>
<table align="center">
<tr>
<td>
<label for="lblCrawlInbox">Crawl inbox</label>
</td>
<td>
<input id="lblCrawlInbox" name="inbox" type="checkbox"
value="inbox"/><!-- outlook job param //-->
</td>
</tr>
<tr>
<td>
<label for="lblCrawlSubfolders">Crawl subfolders</label>
</td>
<td>
<input type="checkbox" id="lblCrawlSubfolders" name="subfolders"
value = "subfolders"/><!-- outlook job param //-->
</td>
</tr>
<tr>
<td>
<label for="lblSubFolderPattern">Pattern for subfolders to include</label>
</td>
<td>
<input type="text" maxlength="20" size="16" id="lblSubFolderPattern"
name="pattern"/><!-- outlook job param //-->
</td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>
Filter criteria patterns
</legend>
<table align="center">
<tr>
<td>
<label for="lblselectFilterCriteria">Filter criteria: </label>
</td>
<td>
<select id="lblselectFilterCriteria" name="selectFilterCriteria">
<option>sender</option>
<option>receiver</option>
<option>subject</option>
</select>
</td>
</tr>
<tr>
<td>
<label for="lblfilterPattern">Pattern: </label>
</td>
<td>
<input id="lblfilterPattern" name="filterPattern"
type="text" size="12" maxlength="20" />
</td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>
Message parts to be stored
</legend>
<table align="center">
<tr>
<td>
<label for="lblselectContentFormat">Transfer HTML or Plain Text: </label>
</td>
<td>
<select id="lblselectContentFormat" name="selectContentFormat">
<option>text</option>
<option>html</option>
<option>both</option>
</select>
</td>
</tr>
<tr>
<td>
<label for="lblselectAttachements">Include attachements: </label>
</td>
<td>
<select id="lblselectAttachements" name="selectAttachements">
<option>Yes</option>
<option>No</option>
</select>
</td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>
Job Interval
</legend>
<table align="center">
<tr>
<td>
<label for="lblmailCrawlIntervaloneTime">One Time: </label>
</td>
<td>
<input type="radio" id="lblmailCrawlIntervaloneTime" name="mailCrawlInterval"
value="oneTime" checked="checked" />
</td>
</tr>
<tr>
<td>
<label for="lblmailCrawlIntervalScheduler">Use Scheduler: </label>
</td>
<td>
<input type="radio" id="lblmailCrawlIntervalScheduler" name="mailCrawlInterval"
value="Scheduler" />
</td>
</tr>
</table>
</fieldset>
<input type="submit" name="startCrawlJob" value="Save and Start" />
</fieldset>
</form>
<div nevow:render="systemMessage"/>
</div>
</div>
</div>
</div>
<div id="sub-section" define-macro="sub-section">
</div>
<div nevow:render="footer_fragment">
</div>
</div>
</body>
</html>

View file

@ -1,41 +0,0 @@
<div class="box">
<h4>Navigation</h4>
<div class="body">
<div class="content even menu-1">
<a href="/" class="">Startpage</a>
</div>
<div class="content odd menu-2">Agent configuration</div>
<div class="content odd menu-3">
<a href="/joboverview" class="">
job overview
</a>
</div>
<div class="content odd menu-3">
<a href="/collectOutlookMails" class="">
add outlook crawl job
</a>
</div>
<div class="content odd menu-3">
<a href="/collectFilesystem" class="">
add filesystem crawl job</a>
</div>
<div class="content odd menu-3">
<a href="/viewRessources" class="">
view collected ressources
</a>
</div>
<div class="content odd menu-3">
<a href="/loggingoptions" class="" title="">
logging options
</a>
</div>
</div>
</div>

View file

@ -1,79 +0,0 @@
<!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" xml:lang="en" lang="en"> //-->
<html xmlns:nevow="http://nevow.com/ns/nevow/0.1">
<!-- Ressource Page for loops.agent UI Version: 0.1 //-->
<nevow:invisible nevow:render="header_fragment" />
<body>
<div class="body">
<div nevow:render="top_fragment">
</div>
<div id="menu">
<div nevow:render="navigation_fragment">
</div>
<br/>
<br/>
<div class="box">
<h4>User Mode</h4>
<div class="body">
<b>Current Mode: </b><p nevow:render="getActiveUserMode"/>
</div>
</div>
</div>
<div id="content">
<div></div>
<div></div>
<div class="content-1" id="2.body" ondblclick="">
<div class="line-block">
<div class="line"><br /></div>
</div>
<div class="section">
<h3><a id="agent-ui-startpage" name="agent-ui-startpage">Agent: collected ressources</a></h3>
<ul class="simple">
<li>All currently available objects that were collected by loops jobs</li>
</ul>
</div>
<div class="section">
<h3><a id="form-overview" name="form-overview">Overview</a></h3>
<ul class="simple">
<li><b>Ressource Collection</b><div nevow:render="data" nevow:data="displayViewForm"/></li>
</ul>
<div nevow:render="systemMessage"/>
<div align="center">
<table class="listing">
<thead>
<p nevow:render="displayRessourceHeaders">
</p>
</thead>
<tbody>
<p nevow:render="displayRessources">
</p>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div id="sub-section" define-macro="sub-section">
</div>
<div nevow:render="footer_fragment">
</div>
</div>
</body>
</html>

View file

@ -1,5 +0,0 @@
<div id="global">
<div class="top" style="padding: 5px">
<img src="/resources/loops_logo.png" />
</div>
</div>

View file

@ -1,21 +0,0 @@
#!C:\Python25\python.exe
# Twisted, the Framework of Your Internet
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
# See LICENSE for details.
### Twisted Preamble
# This makes sure that users don't have to set up their environment
# specially in order to run these programs from bin/.
import sys, os, string
if string.find(os.path.abspath(sys.argv[0]), os.sep+'Twisted') != -1:
sys.path.insert(0, os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), os.pardir, os.pardir)))
if hasattr(os, "getuid") and os.getuid() != 0:
sys.path.insert(0, os.path.abspath(os.getcwd()))
### end of preamble
from twisted.scripts.twistd import run
run()

View file

@ -1 +0,0 @@
UserMode:Simple

File diff suppressed because it is too large Load diff