diff --git a/agent/README.txt b/agent/README.txt deleted file mode 100644 index 4c01b87..0000000 --- a/agent/README.txt +++ /dev/null @@ -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: [] - 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. - diff --git a/agent/__init__.py b/agent/__init__.py deleted file mode 100644 index 4bc90fb..0000000 --- a/agent/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -$Id$ -""" - diff --git a/agent/config.py b/agent/config.py deleted file mode 100644 index ced33a8..0000000 --- a/agent/config.py +++ /dev/null @@ -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))) - diff --git a/agent/core.py b/agent/core.py deleted file mode 100644 index 296bc14..0000000 --- a/agent/core.py +++ /dev/null @@ -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) - diff --git a/agent/crawl/__init__.py b/agent/crawl/__init__.py deleted file mode 100644 index 4bc90fb..0000000 --- a/agent/crawl/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -$Id$ -""" - diff --git a/agent/crawl/base.py b/agent/crawl/base.py deleted file mode 100644 index 04575fe..0000000 --- a/agent/crawl/base.py +++ /dev/null @@ -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 - diff --git a/agent/crawl/filesystem.py b/agent/crawl/filesystem.py deleted file mode 100644 index 45ac986..0000000 --- a/agent/crawl/filesystem.py +++ /dev/null @@ -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') - diff --git a/agent/crawl/filesystem.txt b/agent/crawl/filesystem.txt deleted file mode 100644 index 5bb1302..0000000 --- a/agent/crawl/filesystem.txt +++ /dev/null @@ -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 - diff --git a/agent/crawl/outlook.py b/agent/crawl/outlook.py deleted file mode 100644 index 91d6c75..0000000 --- a/agent/crawl/outlook.py +++ /dev/null @@ -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 - diff --git a/agent/crawl/watsup/AppControls.py b/agent/crawl/watsup/AppControls.py deleted file mode 100644 index dee6f64..0000000 --- a/agent/crawl/watsup/AppControls.py +++ /dev/null @@ -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)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, - - } - \ No newline at end of file diff --git a/agent/crawl/watsup/__init__.py b/agent/crawl/watsup/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/agent/crawl/watsup/docs/WatsupIE methods.doc b/agent/crawl/watsup/docs/WatsupIE methods.doc deleted file mode 100644 index ae7b75c..0000000 Binary files a/agent/crawl/watsup/docs/WatsupIE methods.doc and /dev/null differ diff --git a/agent/crawl/watsup/docs/html/code/example1.py b/agent/crawl/watsup/docs/html/code/example1.py deleted file mode 100644 index a261280..0000000 --- a/agent/crawl/watsup/docs/html/code/example1.py +++ /dev/null @@ -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) - diff --git a/agent/crawl/watsup/docs/html/code/example2.py b/agent/crawl/watsup/docs/html/code/example2.py deleted file mode 100644 index a6bf70c..0000000 --- a/agent/crawl/watsup/docs/html/code/example2.py +++ /dev/null @@ -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() \ No newline at end of file diff --git a/agent/crawl/watsup/docs/html/code/example3.py b/agent/crawl/watsup/docs/html/code/example3.py deleted file mode 100644 index 6f8cb1c..0000000 --- a/agent/crawl/watsup/docs/html/code/example3.py +++ /dev/null @@ -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) - - diff --git a/agent/crawl/watsup/docs/html/code/example4.py b/agent/crawl/watsup/docs/html/code/example4.py deleted file mode 100644 index 8e82bd8..0000000 --- a/agent/crawl/watsup/docs/html/code/example4.py +++ /dev/null @@ -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 - - -Windows Application Test System Using Python - - - - - - -

WATSUP - Windows Application Test System Using Python

- -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. -

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!

- -

The examples in this document assume a basic familiarity with Python.

- - - -

Functional Tests

- -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. -

-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: -

    -
  • Get and set text in editable controls
  • -
  • Edit and select items from controls supporting lists
  • -
  • Click and double-click controls to invoke their actions
  • -
  • Determine the state of menu items and invoke them
  • -
-

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) .

- -

Example 1 - automated writing on Notepad

-

Here's a simple example of the control over applications that you can have with watsup. -First, launch notepad from:

Windows Start Menu - All Programs - Accessories - Notepad

-Then run the following script (Example 1) - -
-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) 
- 
- Finally, close notepad.

- -

Example 2 - testing a simple example

- -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). - -

Now let's script a test to automate this functionality.

- -First find and launch the application. -Then run the following script (Example 2) -
-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()    
-
-
-
-
- - -

Example 3 - automating program launch and termination

- -

It's a bit tedious having to start and close the application each time. -Example 3 launches the application, -if it isn't already running, and terminates it on -completion of the test

- -
-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)    
-
-
- -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. - -

Example 4 - finding windows and controls

- -

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.

- -

In the tools directory, there's a tool - ShowWindows.bat - to assist us with this.

-Show Windows 1 - - -

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.

- -Show Windows 2 - - -

Performance Tests

- -

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").

- -

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.

- -

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. -

- - -

So we have Example 4a

-
-from example4 import main
-
-main('perform.exe','Performance Form 1')
-
-
-

and Example 4b

-
-from example4 import main
-
-main('perform2.exe','Performance Form 2')
-
-
- -

which reference Example 4:

-
-
-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)      
-
-if __name__=='__main__':
-    print ' please run example4a or 4b'
-
-
- -

Key points in example4.py

- -

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:

- -

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.

- -

So in example4.py, we see that the check method requires a 1 second acceptance. -Hence example4a succeeds and example4b fails. -

- -

Regression Testing

- -

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.

-

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: -

- - -

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

- - - -

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

- - -

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. - -

Downloads

- -Download watsup here -

(This package should be unzipped in the site-packages directory in your python installation)

- -

Dependencies

- - -

Credits

- -

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 - Simon Brunning, - 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. -

- -Dr Tim Couper
-timc@tizmoi.net - - - diff --git a/agent/crawl/watsup/docs/images/tizmoi.jpg b/agent/crawl/watsup/docs/images/tizmoi.jpg deleted file mode 100644 index eccd841..0000000 Binary files a/agent/crawl/watsup/docs/images/tizmoi.jpg and /dev/null differ diff --git a/agent/crawl/watsup/docs/tac.css b/agent/crawl/watsup/docs/tac.css deleted file mode 100644 index 3ca6dbd..0000000 --- a/agent/crawl/watsup/docs/tac.css +++ /dev/null @@ -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} - diff --git a/agent/crawl/watsup/launcher.py b/agent/crawl/watsup/launcher.py deleted file mode 100644 index e34441e..0000000 --- a/agent/crawl/watsup/launcher.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/agent/crawl/watsup/performance.py b/agent/crawl/watsup/performance.py deleted file mode 100644 index 8417f58..0000000 --- a/agent/crawl/watsup/performance.py +++ /dev/null @@ -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.startTimeother.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())) - diff --git a/agent/crawl/watsup/timedunittest.py b/agent/crawl/watsup/timedunittest.py deleted file mode 100644 index 6413fc6..0000000 --- a/agent/crawl/watsup/timedunittest.py +++ /dev/null @@ -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.startTimeother.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)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 ' - -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]) - diff --git a/agent/crawl/watsup/tools/fmShowWindows.py b/agent/crawl/watsup/tools/fmShowWindows.py deleted file mode 100644 index 96b5a8f..0000000 --- a/agent/crawl/watsup/tools/fmShowWindows.py +++ /dev/null @@ -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 - diff --git a/agent/crawl/watsup/tools/runTests.ini b/agent/crawl/watsup/tools/runTests.ini deleted file mode 100644 index a528579..0000000 --- a/agent/crawl/watsup/tools/runTests.ini +++ /dev/null @@ -1,4 +0,0 @@ -[main] -checking = True -current_directory = C:\Python23\Lib\site-packages\watsup\examples\unittests - diff --git a/agent/crawl/watsup/tools/showWindows.py b/agent/crawl/watsup/tools/showWindows.py deleted file mode 100644 index 1aafe9d..0000000 --- a/agent/crawl/watsup/tools/showWindows.py +++ /dev/null @@ -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) diff --git a/agent/crawl/watsup/tools/watsup_framework.bat b/agent/crawl/watsup/tools/watsup_framework.bat deleted file mode 100644 index 2ffdf75..0000000 --- a/agent/crawl/watsup/tools/watsup_framework.bat +++ /dev/null @@ -1 +0,0 @@ -python watsup_framework.py \ No newline at end of file diff --git a/agent/crawl/watsup/tools/watsup_framework.py b/agent/crawl/watsup/tools/watsup_framework.py deleted file mode 100644 index f6ff39f..0000000 --- a/agent/crawl/watsup/tools/watsup_framework.py +++ /dev/null @@ -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 - diff --git a/agent/crawl/watsup/tools/wxShowWindows.py b/agent/crawl/watsup/tools/wxShowWindows.py deleted file mode 100644 index 2ec2110..0000000 --- a/agent/crawl/watsup/tools/wxShowWindows.py +++ /dev/null @@ -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() diff --git a/agent/crawl/watsup/utils.py b/agent/crawl/watsup/utils.py deleted file mode 100644 index 79c502a..0000000 --- a/agent/crawl/watsup/utils.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/agent/crawl/watsup/wie.py b/agent/crawl/watsup/wie.py deleted file mode 100644 index 8f5cf37..0000000 --- a/agent/crawl/watsup/wie.py +++ /dev/null @@ -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) - - \ No newline at end of file diff --git a/agent/crawl/watsup/winGuiAuto.py b/agent/crawl/watsup/winGuiAuto.py deleted file mode 100644 index 9ae3f4c..0000000 --- a/agent/crawl/watsup/winGuiAuto.py +++ /dev/null @@ -1,1169 +0,0 @@ -# Module : winGuiAuto.py -# Synopsis : Windows GUI automation utilities -# Programmer : Simon Brunning - simon@brunningonline.net -# Date : 25 June 2003 -# Version : 1.0 -# Copyright : Released to the public domain. Provided as-is, with no warranty. -# Notes : Requires Python 2.3, win32all and ctypes - -# Modifications by Tim Couper - tim@tizmoi.net -# 22 Jul 2004 -# findControls: deduplicates the list to be returned -# findControl: handles win32gui.error from initial call to findControls -# getMenuInfo: improved algorithm for calculating no. of items in a menu -# activateMenuItem: improved algorithm for calculating no. of items in a menu -# -# GLOBALLY corrected spelling: seperator -> separator -# : descendent -> descendant -# added findMenuItem - -'''Windows GUI automation utilities. - -TODO - Until I get around to writing some docs and examples, the tests at the -foot of this module should serve to get you started. - -The standard pattern of usage of winGuiAuto is in three stages; identify a -window, identify a control in the window, trigger an action on the control. - -The end result is always that you wish to have an effect upon some Windows GUI -control. - -The first stage is to identify the window within which the control can be -found. To do this, you can use either findTopWindow or findTopWindows. The -findTopWindow function returns a reference to single window, or throws an -exception if multiple windows or no windows matching the supplied selection -criteria are found. If no matching window is found, immediately, the -findTopWindow function may keep trying to find a match, depending upon suppled -retry aguments. The findTopWindows function returns a list of references to all -windows matching the supplied selection criteria; this list may be empty. The -findTopWindows function will return immediately. - -Usually, specifying caption text, the window's class, or both, will be -sufficient to identify the required window. (Note that the window's class is a -Windows API concept; it has nothing to do with Python classes. See http://msdn.microsoft.com/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/Window_89WindowClasse.asp -for an overview.) Caption text will match if the specified text is *contained* -in the windows' captions; this match is case unsensitive. Window class names -must match identically; the full class name must be matched, and the test is -case sensitive. - -This matching behavior is usually the most useful - but not always. If other -selection behavior is required, a selection function can be passed to the find -functions. - -These returned references are in the form of 'Windows Handles'. All windows and -controls are accessed via a Windows Handle (or hwnd). See -http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/windowing/windows/aboutwindows.asp -for Microsoft's info-deluge on all things Windows window. - -Having identified the window, the next stage is to identify the control. To do -this, you can use either of the findControl and findControls functions. These -work in almost exactly the same way as the findTopWindow and findTopWindows -functions; they take the hwnd of the top level window within which the required -control should be found, and otherwise take the same arguments as the -findTopWindow and findTopWindows functions, and work in the same ways. - -Having now identified your control, you can now query its state, and send -actions (in the form of Windows messages) to it. It's at this point that you -are most likely to end up having to extend winGuiAuto in some way; the -developer of the GUI that you are trying to drive is free to develop controls -which respond to different messages in different ways. Still, if standard MFC -controls have been used, you may find that winGuiAuto's functions may provide -much or all of what you need - or at least provide a useful template. These -functions (clickButton, clickStatic, doubleClickStatic, getComboboxItems, -getEditText, getListboxItems, selectComboboxItem, selectListboxItem and -setEditText) should be fairly self evident. - -Selection of menu items is a slightly different process: but don't fret; it's -pretty simple. You'll need to identify the hwnd of the top level window that -you want to control using either of the findTopWindow and findTopWindows -functions. You can then call the activateMenuItem function, passing in the top -level window's hwnd and the path to the menu option that you wish to activate. -This path should consist of a list specifiying the path to the required menu -option - see activateMenuItem's docstring for details. - -TODO getMenuInfo - -TODO: dumpTopWindows, dumpWindow, Spy++ and Winspector (http://www.windows-spy.com) -Two if the main difficulties you'll come across while using winGuiAuto will be discovering - the classes of the windows that you want, and where the provided -''' - -import array -import ctypes -import os -import struct -import sys -import time -import win32api -import win32con -import win32gui - -def findTopWindow(wantedText=None, - wantedClass=None, - selectionFunction=None, - maxWait=1, - retryInterval=0.1): - '''Find the hwnd of a top level window. - - You can identify windows using captions, classes, a custom selection - function, or any combination of these. (Multiple selection criteria are - ANDed. If this isn't what's wanted, use a selection function.) - - If no window matching the specified selection criteria is found - immediately, further attempts will be made. The retry interval and maximum - time to wait for a matching window can be specified. - - Arguments: - - wantedText Text which the required window's caption must - *contain*. (Case insensitive match.) - wantedClass Class to which the required window must belong. - selectionFunction Window selection function. Reference to a function - should be passed here. The function should take hwnd - as an argument, and should return True when passed the - hwnd of a desired window. - maxWait The maximum time to wait for a matching window, in - seconds. - retryInterval How frequently to look for a matching window, in - seconds - - Raises: - WinGuiAutoError When no window or multiple windows found. - - Usage examples: # Caption contains "Options" - optDialog = findTopWindow(wantedText="Options") - # Caption *equals* "Notepad" - saveDialog = findTopWindow(selectionFunction=lambda hwnd: - win32gui.GetWindowText(hwnd)=='Notepad') - ''' - topWindows = findTopWindows(wantedText, wantedClass, selectionFunction) - if len(topWindows) > 1: - raise WinGuiAutoError("Multiple top level windows found for wantedText=" + - repr(wantedText) + - ", wantedClass=" + - repr(wantedClass) + - ", selectionFunction=" + - repr(selectionFunction) + - ", maxWait=" + - repr(maxWait) + - ", retryInterval=" + - str(retryInterval)) - elif topWindows: - return topWindows[0] - elif (maxWait-retryInterval) >= 0: - time.sleep(retryInterval) - try: - return findTopWindow(wantedText=wantedText, - wantedClass=wantedClass, - selectionFunction=selectionFunction, - maxWait=maxWait-retryInterval, - retryInterval=retryInterval) - except WinGuiAutoError: - raise WinGuiAutoError("No top level window found for wantedText=" + - repr(wantedText) + - ", wantedClass=" + - repr(wantedClass) + - ", selectionFunction=" + - repr(selectionFunction) + - ", maxWait=" + - repr(maxWait) + - ", retryInterval=" + - str(retryInterval)) - else: - raise WinGuiAutoError("No top level window found for wantedText=" + - repr(wantedText) + - ", wantedClass=" + - repr(wantedClass) + - ", selectionFunction=" + - repr(selectionFunction) + - ", maxWait=" + - repr(maxWait) + - ", retryInterval=" + - str(retryInterval)) - -def findTopWindows(wantedText=None, wantedClass=None, selectionFunction=None): - '''Find the hwnd of top level windows. - - You can identify windows using captions, classes, a custom selection - function, or any combination of these. (Multiple selection criteria are - ANDed. If this isn't what's wanted, use a selection function.) - - Arguments: - wantedText Text which required windows' captions must contain. - wantedClass Class to which required windows must belong. - selectionFunction Window selection function. Reference to a function - should be passed here. The function should take hwnd as - an argument, and should return True when passed the - hwnd of a desired window. - - Returns: A list containing the window handles of all top level - windows matching the supplied selection criteria. - - Usage example: optDialogs = findTopWindows(wantedText="Options") - ''' - results = [] - topWindows = [] - win32gui.EnumWindows(_windowEnumerationHandler, topWindows) - for hwnd, windowText, windowClass in topWindows: - if wantedText and \ - not _normaliseText(wantedText) in _normaliseText(windowText): - continue - if wantedClass and not windowClass == wantedClass: - continue - if selectionFunction and not selectionFunction(hwnd): - continue - results.append(hwnd) - return results - -def dumpTopWindows(): - '''TODO''' - topWindows = [] - win32gui.EnumWindows(_windowEnumerationHandler, topWindows) - return topWindows - -def dumpWindow(hwnd): - '''Dump all controls from a window into a nested list - - Useful during development, allowing to you discover the structure of the - contents of a window, showing the text and class of all contained controls. - - Think of it as a poor man's Spy++. ;-) - - Arguments: The window handle of the top level window to dump. - - Returns A nested list of controls. Each entry consists of the - control's hwnd, its text, its class, and its sub-controls, - if any. - - Usage example: replaceDialog = findTopWindow(wantedText='Replace') - pprint.pprint(dumpWindow(replaceDialog)) - ''' - windows = [] - try: - win32gui.EnumChildWindows(hwnd, _windowEnumerationHandler, windows) - except win32gui.error: - # No child windows - return - windows = [list(window) for window in windows] - for window in windows: - childHwnd, windowText, windowClass = window - window_content = dumpWindow(childHwnd) - if window_content: - window.append(window_content) - - def dedup(thelist): - '''De-duplicate deeply nested windows list.''' - def listContainsSublists(thelist): - return bool([sublist - for sublist in thelist - if isinstance(sublist, list)]) - found=[] - def dodedup(thelist): - todel = [] - for index, thing in enumerate(thelist): - if isinstance(thing, list) and listContainsSublists(thing): - dodedup(thing) - else: - if thing in found: - todel.append(index) - else: - found.append(thing) - todel.reverse() - for todel in todel: - del thelist[todel] - dodedup(thelist) - dedup(windows) - - return windows - -def findControl(topHwnd, - wantedText=None, - wantedClass=None, - selectionFunction=None, - maxWait=1, - retryInterval=0.1): - '''Find a control. - - You can identify a control within a top level window, using caption, class, - a custom selection function, or any combination of these. (Multiple - selection criteria are ANDed. If this isn't what's wanted, use a selection - function.) - - If no control matching the specified selection criteria is found - immediately, further attempts will be made. The retry interval and maximum - time to wait for a matching control can be specified. - - Arguments: - topHwnd The window handle of the top level window in which the - required controls reside. - wantedText Text which the required control's captions must contain. - wantedClass Class to which the required control must belong. - selectionFunction Control selection function. Reference to a function - should be passed here. The function should take hwnd as - an argument, and should return True when passed the - hwnd of the desired control. - maxWait The maximum time to wait for a matching control, in - seconds. - retryInterval How frequently to look for a matching control, in - seconds - - Returns: The window handle of the first control matching the - supplied selection criteria. - - Raises: - WinGuiAutoError When no control or multiple controls found. - - Usage example: optDialog = findTopWindow(wantedText="Options") - okButton = findControl(optDialog, - wantedClass="Button", - wantedText="OK") - ''' - controls = findControls(topHwnd, - wantedText=wantedText, - wantedClass=wantedClass, - selectionFunction=selectionFunction) - # check for None returned: Tim 6 Jul 2004 - if controls==None: - raise WinGuiAutoError("EnumChildWindows failed with win32gui.error " + - repr(topHwnd) + - ", wantedText=" + - repr(wantedText) + - ", wantedClass=" + - repr(wantedClass) + - ", selectionFunction=" + - repr(selectionFunction) + - ", maxWait=" + - repr(maxWait) + - ", retryInterval=" + - str(retryInterval) - ) - - if len(controls) > 1: - raise WinGuiAutoError("Multiple controls found for topHwnd=" + - repr(topHwnd) + - ", wantedText=" + - repr(wantedText) + - ", wantedClass=" + - repr(wantedClass) + - ", selectionFunction=" + - repr(selectionFunction) + - ", maxWait=" + - repr(maxWait) + - ", retryInterval=" + - str(retryInterval)) - elif controls: - return controls[0] - elif (maxWait-retryInterval) >= 0: - time.sleep(retryInterval) - try: - return findControl(topHwnd=topHwnd, - wantedText=wantedText, - wantedClass=wantedClass, - selectionFunction=selectionFunction, - maxWait=maxWait-retryInterval, - retryInterval=retryInterval) - except WinGuiAutoError: - raise WinGuiAutoError("No control found for topHwnd=" + - repr(topHwnd) + - ", wantedText=" + - repr(wantedText) + - ", wantedClass=" + - repr(wantedClass) + - ", selectionFunction=" + - repr(selectionFunction) + - ", maxWait=" + - repr(maxWait) + - ", retryInterval=" + - str(retryInterval)) - else: - raise WinGuiAutoError("No control found for topHwnd=" + - repr(topHwnd) + - ", wantedText=" + - repr(wantedText) + - ", wantedClass=" + - repr(wantedClass) + - ", selectionFunction=" + - repr(selectionFunction) + - ", maxWait=" + - repr(maxWait) + - ", retryInterval=" + - str(retryInterval)) - -def findControls(topHwnd, - wantedText=None, - wantedClass=None, - selectionFunction=None): - '''Find controls. - - You can identify controls using captions, classes, a custom selection - function, or any combination of these. (Multiple selection criteria are - ANDed. If this isn't what's wanted, use a selection function.) - - Arguments: - topHwnd The window handle of the top level window in which the - required controls reside. - wantedText Text which the required controls' captions must contain. - wantedClass Class to which the required controls must belong. - selectionFunction Control selection function. Reference to a function - should be passed here. The function should take hwnd as - an argument, and should return True when passed the - hwnd of a desired control. - - Returns: The window handles of the controls matching the - supplied selection criteria. - - Usage example: optDialog = findTopWindow(wantedText="Options") - def findButtons(hwnd, windowText, windowClass): - return windowClass == "Button" - buttons = findControl(optDialog, wantedText="Button") - ''' - def searchChildWindows(currentHwnd): - results = [] - childWindows = [] - try: - win32gui.EnumChildWindows(currentHwnd, - _windowEnumerationHandler, - childWindows) - except win32gui.error: - # This seems to mean that the control *cannot* have child windows, - # i.e. is not a container. - return - - for childHwnd, windowText, windowClass in childWindows: - descendantMatchingHwnds = searchChildWindows(childHwnd) - if descendantMatchingHwnds: - results += descendantMatchingHwnds - - if wantedText and \ - not _normaliseText(wantedText) in _normaliseText(windowText): - continue - if wantedClass and \ - not windowClass == wantedClass: - continue - if selectionFunction and \ - not selectionFunction(childHwnd): - continue - results.append(childHwnd) - return results - - # deduplicate the returned windows: Tim 6 Jul 2004 - #return searchChildWindows(topHwnd) - - hlist=searchChildWindows(topHwnd) - - if hlist: - # deduplicate the list: - hdict={} - for h in hlist: - hdict[h]='' - return hdict.keys() - else: - return hlist - -def getTopMenu(hWnd): - '''Get a window's main, top level menu. - - Arguments: - hWnd The window handle of the top level window for which the top - level menu is required. - - Returns: The menu handle of the window's main, top level menu. - - Usage example: hMenu = getTopMenu(hWnd) - ''' - return ctypes.windll.user32.GetMenu(ctypes.c_long(hWnd)) - -def activateMenuItem(hWnd, menuItemPath): - '''Activate a menu item - - Arguments: - hWnd The window handle of the top level window whose menu you - wish to activate. - menuItemPath The path to the required menu item. This should be a - sequence specifying the path through the menu to the - required item. Each item in this path can be specified - either as an index, or as a menu name. - - Raises: - WinGuiAutoError When the requested menu option isn't found. - - Usage example: activateMenuItem(notepadWindow, ('file', 'open')) - - Which is exactly equivalent to... - - activateMenuItem(notepadWindow, (0, 1)) - ''' - # By Axel Kowald (kowald@molgen.mpg.de) - # Modified by S Brunning to accept strings in addition to indicies. - - # Top level menu - hMenu = getTopMenu(hWnd) - - # Get top level menu's item count. Is there a better way to do this? - # Yes .. Tim Couper 22 Jul 2004 - - hMenuItemCount=win32gui.GetMenuItemCount(hMenu) - -## for hMenuItemCount in xrange(256): -## try: -## _getMenuInfo(hMenu, hMenuItemCount) -## except WinGuiAutoError: -## break -## hMenuItemCount -= 1 - - # Walk down submenus - for submenu in menuItemPath[:-1]: - try: # submenu is an index - 0 + submenu - submenuInfo = _getMenuInfo(hMenu, submenu) - hMenu, hMenuItemCount = submenuInfo.submenu, submenuInfo.itemCount - except TypeError: # Hopefully, submenu is a menu name - try: - dump, hMenu, hMenuItemCount = _findNamedSubmenu(hMenu, - hMenuItemCount, - submenu) - except WinGuiAutoError: - raise WinGuiAutoError("Menu path " + - repr(menuItemPath) + - " cannot be found.") - - # Get required menu item's ID. (the one at the end). - menuItem = menuItemPath[-1] - try: # menuItem is an index - 0 + menuItem - menuItemID = ctypes.windll.user32.GetMenuItemID(hMenu, - menuItem) - except TypeError: # Hopefully, menuItem is a menu name - try: - subMenuIndex, dump, dump = _findNamedSubmenu(hMenu, - hMenuItemCount, - menuItem) - except WinGuiAutoError: - raise WinGuiAutoError("Menu path " + - repr(menuItemPath) + - " cannot be found.") - menuItemID = ctypes.windll.user32.GetMenuItemID(hMenu, subMenuIndex) - - # Activate - win32gui.PostMessage(hWnd, win32con.WM_COMMAND, menuItemID, 0) - -##def findMenuItems(hWnd,wantedText): -## """Finds menu items whose captions contain the text""" -## hMenu = getTopMenu(hWnd) -## hMenuItemCount=win32gui.GetMenuItemCount(hMenu) -## -## for topItem in xrange(hMenuItemCount): -## - -def getMenuInfo(hWnd, menuItemPath): - '''TODO''' - - # Top level menu - hMenu = getTopMenu(hWnd) - - # Get top level menu's item count. Is there a better way to do this? - # Yes .. Tim Couper 22 Jul 2004 - hMenuItemCount=win32gui.GetMenuItemCount(hMenu) - -## for hMenuItemCount in xrange(256): -## try: -## _getMenuInfo(hMenu, hMenuItemCount) -## except WinGuiAutoError: -## break -## hMenuItemCount -= 1 - submenuInfo=None - - # Walk down submenus - for submenu in menuItemPath: - try: # submenu is an index - 0 + submenu - submenuInfo = _getMenuInfo(hMenu, submenu) - hMenu, hMenuItemCount = submenuInfo.submenu, submenuInfo.itemCount - except TypeError: # Hopefully, submenu is a menu name - try: - submenuIndex, new_hMenu, hMenuItemCount = _findNamedSubmenu(hMenu, - hMenuItemCount, - submenu) - submenuInfo = _getMenuInfo(hMenu, submenuIndex) - hMenu = new_hMenu - except WinGuiAutoError: - raise WinGuiAutoError("Menu path " + - repr(menuItemPath) + - " cannot be found.") - if submenuInfo==None: - raise WinGuiAutoError("Menu path " + - repr(menuItemPath) + - " cannot be found. (Null menu path?)") - - - return submenuInfo - -def _getMenuInfo(hMenu, uIDItem): - '''Get various info about a menu item. - - Arguments: - hMenu The menu in which the item is to be found. - uIDItem The item's index - - Returns: Menu item information object. This object is basically - a 'bunch' - (see http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52308). - It will have useful attributes: name, itemCount, - submenu, isChecked, isDisabled, isGreyed, and - isSeparator - - Raises: - WinGuiAutoError When the requested menu option isn't found. - - Usage example: submenuInfo = _getMenuInfo(hMenu, submenu) - hMenu, hMenuItemCount = submenuInfo.submenu, submenuInfo.itemCount - ''' - # An object to hold the menu info - class MenuInfo(Bunch): - pass - menuInfo = MenuInfo() - - # Menu state - menuState = ctypes.windll.user32.GetMenuState(hMenu, - uIDItem, - win32con.MF_BYPOSITION) - if menuState == -1: - raise WinGuiAutoError("No such menu item, hMenu=" + - str(hMenu) + - " uIDItem=" + - str(uIDItem)) - - # Menu name - menuName = ctypes.c_buffer("\000" * 32) - ctypes.windll.user32.GetMenuStringA(ctypes.c_int(hMenu), - ctypes.c_int(uIDItem), - menuName, ctypes.c_int(len(menuName)), - win32con.MF_BYPOSITION) - menuInfo.name = menuName.value - - # Sub menu info - menuInfo.itemCount = menuState >> 8 - if bool(menuState & win32con.MF_POPUP): - menuInfo.submenu = ctypes.windll.user32.GetSubMenu(hMenu, uIDItem) - else: - menuInfo.submenu = None - - menuInfo.isChecked = bool(menuState & win32con.MF_CHECKED) - menuInfo.isDisabled = bool(menuState & win32con.MF_DISABLED) - menuInfo.isGreyed = bool(menuState & win32con.MF_GRAYED) - menuInfo.isSeparator = bool(menuState & win32con.MF_SEPARATOR) - # ... there are more, but these are the ones I'm interested in thus far. - - return menuInfo - -def clickButton(hwnd): - '''Simulates a single mouse click on a button - - Arguments: - hwnd Window handle of the required button. - - Usage example: okButton = findControl(fontDialog, - wantedClass="Button", - wantedText="OK") - clickButton(okButton) - ''' - _sendNotifyMessage(hwnd, win32con.BN_CLICKED) - -def clickStatic(hwnd): - '''Simulates a single mouse click on a static - - Arguments: - hwnd Window handle of the required static. - - Usage example: TODO - ''' - _sendNotifyMessage(hwnd, win32con.STN_CLICKED) - -def doubleClickStatic(hwnd): - '''Simulates a double mouse click on a static - - Arguments: - hwnd Window handle of the required static. - - Usage example: TODO - ''' - _sendNotifyMessage(hwnd, win32con.STN_DBLCLK) - -def getComboboxItems(hwnd): - '''Returns the items in a combo box control. - - Arguments: - hwnd Window handle for the combo box. - - Returns: Combo box items. - - Usage example: fontCombo = findControl(fontDialog, wantedClass="ComboBox") - fontComboItems = getComboboxItems(fontCombo) - ''' - - return _getMultipleWindowValues(hwnd, - getCountMessage=win32con.CB_GETCOUNT, - getValueMessage=win32con.CB_GETLBTEXT) - -def selectComboboxItem(hwnd, item): - '''Selects a specified item in a Combo box control. - - Arguments: - hwnd Window handle of the required combo box. - item The reqired item. Either an index, of the text of the - required item. - - Usage example: fontComboItems = getComboboxItems(fontCombo) - selectComboboxItem(fontCombo, - random.choice(fontComboItems)) - ''' - try: # item is an index Use this to select - 0 + item - win32gui.SendMessage(hwnd, win32con.CB_SETCURSEL, item, 0) - _sendNotifyMessage(hwnd, win32con.CBN_SELCHANGE) - except TypeError: # Item is a string - find the index, and use that - items = getComboboxItems(hwnd) - itemIndex = items.index(item) - selectComboboxItem(hwnd, itemIndex) - -def getListboxItems(hwnd): - '''Returns the items in a list box control. - - Arguments: - hwnd Window handle for the list box. - - Returns: List box items. - - Usage example: docType = findControl(newDialog, wantedClass="ListBox") - typeListBox = getListboxItems(docType) - ''' - - return _getMultipleWindowValues(hwnd, - getCountMessage=win32con.LB_GETCOUNT, - getValueMessage=win32con.LB_GETTEXT) - -def selectListboxItem(hwnd, item): - '''Selects a specified item in a list box control. - - Arguments: - hwnd Window handle of the required list box. - item The reqired item. Either an index, of the text of the - required item. - - Usage example: docType = findControl(newDialog, wantedClass="ListBox") - typeListBox = getListboxItems(docType) - - # Select a type at random - selectListboxItem(docType, - random.randint(0, len(typeListBox)-1)) - ''' - try: # item is an index Use this to select - 0 + item - win32gui.SendMessage(hwnd, win32con.LB_SETCURSEL, item, 0) - _sendNotifyMessage(hwnd, win32con.LBN_SELCHANGE) - except TypeError: # Item is a string - find the index, and use that - items = getListboxItems(hwnd) - itemIndex = items.index(item) - selectListboxItem(hwnd, itemIndex) - -def getEditText(hwnd): - '''Returns the text in an edit control. - - Arguments: - hwnd Window handle for the edit control. - - Returns Edit control text lines. - - Usage example: pprint.pprint(getEditText(editArea)) - ''' - return _getMultipleWindowValues(hwnd, - getCountMessage=win32con.EM_GETLINECOUNT, - getValueMessage=win32con.EM_GETLINE) - -def setEditText(hwnd, text, append=False): - '''Set an edit control's text. - - Arguments: - hwnd The edit control's hwnd. - text The text to send to the control. This can be a single - string, or a sequence of strings. If the latter, each will - be become a a separate line in the control. - append Should the new text be appended to the existing text? - Defaults to False, meaning that any existing text will be - replaced. If True, the new text will be appended to the end - of the existing text. - Note that the first line of the new text will be directly - appended to the end of the last line of the existing text. - If appending lines of text, you may wish to pass in an - empty string as the 1st element of the 'text' argument. - - Usage example: print "Enter various bits of text." - setEditText(editArea, "Hello, again!") - setEditText(editArea, "You still there?") - setEditText(editArea, ["Here come", "two lines!"]) - - print "Add some..." - setEditText(editArea, ["", "And a 3rd one!"], append=True) - ''' - - # Ensure that text is a list - try: - text + '' - text = [text] - except TypeError: - pass - - # Set the current selection range, depending on append flag - if append: - win32gui.SendMessage(hwnd, - win32con.EM_SETSEL, - -1, - 0) - else: - win32gui.SendMessage(hwnd, - win32con.EM_SETSEL, - 0, - -1) - - # Send the text - win32gui.SendMessage(hwnd, - win32con.EM_REPLACESEL, - True, - os.linesep.join(text)) - -def setCheckBox(hwnd, state = True): - """ - Activates a CheckBox button. - - Inputs - - hwnd - Handle of GUI element - state - Boolean - True - Activate the Checkbox - False - Clear the CheckBox - - Outputs - - Integer -- Result of the Win32gui.SendMessage Command - - Note: There is a 3rd state to a CheckBox. Since it is not - common has been split to another function - setCheckBox_Indeterminate. - """ - win32gui.SendMessage( hwnd, - win32con.BM_SETCHECK, - win32con.BST_CHECKED, - 0 ) - - -def setCheckBox_Indeterminate(hwnd): - """ - Activates a CheckBox button. - - Inputs - - hwnd - Handle of GUI element - - Outputs - - Integer -- Result of the Win32gui.SendMessage Command - - """ - win32gui.SendMessage( hwnd, - win32con.BM_SETCHECK, - win32con.BST_INDETERMINATE, - 0 ) - -def getCheckBox(hwnd): - """ - Returns the status from a CheckBox button. - - Inputs - - hwnd - Handle of GUI element - - Outputs - - 0 - win32Gui send message error - win32con.BST_CHECKED- The Checkbox is checked - win32con.BST_INDETERMINATE - The Checkbox is checked and = - greyed out. - win32con.BST_UNCHECKED- The checkbox is not checked - =20 - """ - value = win32gui.SendMessage( hwnd, - win32con.BM_GETCHECK, - 0, - 0 ) - return value - -def _getMultipleWindowValues(hwnd, getCountMessage, getValueMessage): - ''' - - A common pattern in the Win32 API is that in order to retrieve a - series of values, you use one message to get a count of available - items, and another to retrieve them. This internal utility function - performs the common processing for this pattern. - - Arguments: - hwnd Window handle for the window for which items should be - retrieved. - getCountMessage Item count message. - getValueMessage Value retrieval message. - - Returns: Retrieved items. - ''' - result = [] - - MAX_VALUE_LENGTH = 256 - bufferlength = struct.pack('i', MAX_VALUE_LENGTH) # This is a C style int. - - valuecount = win32gui.SendMessage(hwnd, getCountMessage, 0, 0) - for itemIndex in range(valuecount): - valuebuffer = array.array('c', - bufferlength + - ' ' * (MAX_VALUE_LENGTH - len(bufferlength))) - valueLength = win32gui.SendMessage(hwnd, - getValueMessage, - itemIndex, - valuebuffer) - result.append(valuebuffer.tostring()[:valueLength]) - return result - -def _windowEnumerationHandler(hwnd, resultList): - '''win32gui.EnumWindows() callback. - - Pass to win32gui.EnumWindows() or win32gui.EnumChildWindows() to - generate a list of window handle, window text, window class tuples. - ''' - resultList.append((hwnd, - win32gui.GetWindowText(hwnd), - win32gui.GetClassName(hwnd))) - -def _buildWinLong(high, low): - '''Build a windows long parameter from high and low words. - - See http://support.microsoft.com/support/kb/articles/q189/1/70.asp - ''' - # return ((high << 16) | low) - return int(struct.unpack('>L', - struct.pack('>2H', - high, - low)) [0]) - -def _sendNotifyMessage(hwnd, notifyMessage): - '''Send a notify message to a control.''' - win32gui.SendMessage(win32gui.GetParent(hwnd), - win32con.WM_COMMAND, - _buildWinLong(notifyMessage, - win32api.GetWindowLong(hwnd, - win32con.GWL_ID)), - hwnd) - -def _normaliseText(controlText): - '''Remove '&' characters, and lower case. - - Useful for matching control text.''' - return controlText.lower().replace('&', '') - -def _findNamedSubmenu(hMenu, hMenuItemCount, submenuName): - '''Find the index number of a menu's submenu with a specific name.''' - for submenuIndex in range(hMenuItemCount): - submenuInfo = _getMenuInfo(hMenu, submenuIndex) - if _normaliseText(submenuInfo.name).startswith(_normaliseText(submenuName)): - return submenuIndex, submenuInfo.submenu, submenuInfo.itemCount - raise WinGuiAutoError("No submenu found for hMenu=" + - repr(hMenu) + - ", hMenuItemCount=" + - repr(hMenuItemCount) + - ", submenuName=" + - repr(submenuName)) - -def _dedup(thelist): - '''De-duplicate deeply nested list.''' - found=[] - def dodedup(thelist): - for index, thing in enumerate(thelist): - if isinstance(thing, list): - dodedup(thing) - else: - if thing in found: - del thelist[index] - else: - found.append(thing) - - -class Bunch(object): - '''See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52308''' - - def __init__(self, **kwds): - self.__dict__.update(kwds) - - def __str__(self): - state = ["%s=%r" % (attribute, value) - for (attribute, value) - in self.__dict__.items()] - return '\n'.join(state) - -class WinGuiAutoError(Exception): - pass - -def self_test(): - '''Self test - drives Notepad and WordPad.''' - # I'd like to use unittest here, but I've no idea how to automate these - # tests. As it is, you just have to *watch* the test! ;-) - import pprint, random - - # NT/2K/XP versions of Notepad have a different menu stuctures. - WIN_VERSION_DECODE = {(1, 4, 0): "95", - (1, 4, 10): "98", - (1, 4, 90): "ME", - (2, 4, 0): "NT", - (2, 5, 0): "2K", - (2, 5, 1): "XP"} - win_version = os.sys.getwindowsversion() - win_version = WIN_VERSION_DECODE[win_version[3], win_version[0], win_version[1]] - print "win_version=", win_version - x=raw_input('->') - print "Let's see what top windows we have at the 'mo:" - pprint.pprint(dumpTopWindows()) - x=raw_input('->') - print "Open and locate Notepad" - os.startfile('notepad') - notepadWindow = findTopWindow(wantedClass='Notepad') - x=raw_input('->') - print "Look at some menu item info" - - print 'File menu' - print getMenuInfo(notepadWindow, ('file',)) - print "file|exit" - print getMenuInfo(notepadWindow, ('file', 'exit')) - x=raw_input('->') - print "Open and locate the 'replace' dialogue" - if win_version in ["NT"]: - activateMenuItem(notepadWindow, ['search', 'replace']) - elif win_version in ["2K", "XP"]: - activateMenuItem(notepadWindow, ['edit', 'replace']) - else: - raise Exception("Tests not written for this OS yet. Feel free!") - replaceDialog = findTopWindow(wantedText='Replace', wantedClass="#32770") - x=raw_input('->') - print "Locate the 'find' edit box" - findValue = findControls(replaceDialog, wantedClass="Edit")[0] - x=raw_input('->') - print "Enter some text - and wait long enough for it to be seen" - setEditText(findValue, "Hello, mate!") - time.sleep(.5) - x=raw_input('->') - - print "Locate the 'cancel' button, and click it." - cancelButton = findControl(replaceDialog, - wantedClass="Button", - wantedText="Cancel") - clickButton(cancelButton) - x=raw_input('->') - print "Open and locate the 'font' dialogue" - if win_version in ["NT"]: - activateMenuItem(notepadWindow, ['edit', 'set font']) - elif win_version in ["2K", "XP"]: - activateMenuItem(notepadWindow, ['format', 'font']) - print findTopWindows(wantedText='Font', wantedClass="#32770") - x=raw_input('->') - fontDialog = findTopWindow(wantedText='Font', wantedClass="#32770") - - print "Let's see if dumping works. Dump the 'font' dialogue contents:" - pprint.pprint(dumpWindow(fontDialog)) - x=raw_input('->') - - print "Change the font" - fontCombos = findControls(fontDialog, wantedClass="ComboBox") - print "Find the font selection combo" - for fontCombo in fontCombos: - fontComboItems = getComboboxItems(fontCombo) - if 'Arial' in fontComboItems: - break - x=raw_input('->') - print "Select at random" - selectComboboxItem(fontCombo, random.choice(fontComboItems)) - time.sleep(.5) - - okButton = findControl(fontDialog, wantedClass="Button", wantedText="OK") - clickButton(okButton) - x=raw_input('->') - - print "Locate notepad's edit area, and enter various bits of text." - editArea = findControl(notepadWindow,wantedClass="Edit") - setEditText(editArea, "Hello, again!") - time.sleep(.5) - setEditText(editArea, "You still there?") - time.sleep(.5) - setEditText(editArea, ["Here come", "two lines!"]) - time.sleep(.5) - x=raw_input('->') - - print "Add some..." - setEditText(editArea, ["", "And a 3rd one!"], append=True) - time.sleep(.5) - - print "See what's there now:" - pprint.pprint(getEditText(editArea)) - x=raw_input('->') - - print "Exit notepad" - activateMenuItem(notepadWindow, ('file', 'exit')) - time.sleep(.5) - - print "Don't save." - saveDialog = findTopWindow(selectionFunction=lambda hwnd: - win32gui.GetWindowText(hwnd)=='Notepad') - noButton = findControl(saveDialog,wantedClass="Button", wantedText="no") - clickButton(noButton) - x=raw_input('->') - - print "Check you get an exception for non-existent top window" - try: - findTopWindow(wantedText="Banana") - raise Exception("Test failed") - except WinGuiAutoError, winGuiAutoError: - print "Yup, got: ", str(winGuiAutoError) - x=raw_input('->') - print "OK, now we'll have a go with WordPad." - os.startfile('wordpad') - wordpadWindow = findTopWindow(wantedText='WordPad') - x=raw_input('->') - print "Open and locate the 'new document' dialog." - activateMenuItem(wordpadWindow, [0, 0]) - newDialog = findTopWindow(wantedText='New', wantedClass="#32770") - x=raw_input('->') - print "Check you get an exception for non-existent control" - try: - findControl(newDialog, wantedClass="Banana") - raise Exception("Test failed") - except WinGuiAutoError, winGuiAutoError: - print "Yup, got: ", str(winGuiAutoError) - x=raw_input('->') - print "Locate the 'document type' list box" - docType = findControl(newDialog, wantedClass="ListBox") - typeListBox = getListboxItems(docType) - print "getListboxItems(docType)=", typeListBox - x=raw_input('->') - print "Select a type at random" - selectListboxItem(docType, random.randint(0, len(typeListBox)-1)) - time.sleep(.5) - clickButton(findControl(newDialog, wantedClass="Button", wantedText="OK")) - x=raw_input('->') - print "Check you get an exception for non-existent menu path" - try: - activateMenuItem(wordpadWindow, ('not', 'there')) - raise Exception("Test failed") - except WinGuiAutoError, winGuiAutoError: - print "Yup, got: ", str(winGuiAutoError) - x=raw_input('->') - print "Check you get an exception for non-existent menu item" - try: - activateMenuItem(wordpadWindow, ('file', 'missing')) - raise Exception("Test failed") - except WinGuiAutoError, winGuiAutoError: - print "Yup, got: ", str(winGuiAutoError) - x=raw_input('->') - print "Exit wordpad" - activateMenuItem(wordpadWindow, ('file', 'exit')) - x=raw_input('->') - print "Err, that's it." - -if __name__ == '__main__': - self_test() \ No newline at end of file diff --git a/agent/interfaces.py b/agent/interfaces.py deleted file mode 100644 index ff2db0d..0000000 --- a/agent/interfaces.py +++ /dev/null @@ -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. - """ - diff --git a/agent/log.py b/agent/log.py deleted file mode 100644 index 6732edf..0000000 --- a/agent/log.py +++ /dev/null @@ -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)) - diff --git a/agent/loops.tac b/agent/loops.tac deleted file mode 100644 index 96a86fd..0000000 --- a/agent/loops.tac +++ /dev/null @@ -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) - diff --git a/agent/schedule.py b/agent/schedule.py deleted file mode 100644 index 49794a1..0000000 --- a/agent/schedule.py +++ /dev/null @@ -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.') - diff --git a/agent/testing/__init__.py b/agent/testing/__init__.py deleted file mode 100644 index 4bc90fb..0000000 --- a/agent/testing/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -$Id$ -""" - diff --git a/agent/testing/client.py b/agent/testing/client.py deleted file mode 100644 index 40ecff8..0000000 --- a/agent/testing/client.py +++ /dev/null @@ -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.' diff --git a/agent/testing/crawl.py b/agent/testing/crawl.py deleted file mode 100644 index 66952d5..0000000 --- a/agent/testing/crawl.py +++ /dev/null @@ -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 diff --git a/agent/testing/data/file1.txt b/agent/testing/data/file1.txt deleted file mode 100644 index 02c267f..0000000 --- a/agent/testing/data/file1.txt +++ /dev/null @@ -1 +0,0 @@ -Data from file1.txt \ No newline at end of file diff --git a/agent/testing/data/subdir/file2.txt b/agent/testing/data/subdir/file2.txt deleted file mode 100644 index 493d31b..0000000 --- a/agent/testing/data/subdir/file2.txt +++ /dev/null @@ -1 +0,0 @@ -Data from file2.txt \ No newline at end of file diff --git a/agent/testing/server.py b/agent/testing/server.py deleted file mode 100644 index 5c48333..0000000 --- a/agent/testing/server.py +++ /dev/null @@ -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('

Hello World

') - self.write('

dir(self): %s

' % dir(self)) - self.write('

self.path: %s

' % self.path) - self.write('

self.uri: %s

' % self.uri) - self.write('

self.args: %s

' % 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) diff --git a/agent/testing/testing.cfg b/agent/testing/testing.cfg deleted file mode 100644 index 133d2f6..0000000 --- a/agent/testing/testing.cfg +++ /dev/null @@ -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' diff --git a/agent/testing/transport.py b/agent/testing/transport.py deleted file mode 100644 index 3cd7d35..0000000 --- a/agent/testing/transport.py +++ /dev/null @@ -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') - diff --git a/agent/tests.py b/agent/tests.py deleted file mode 100755 index 0b81ce0..0000000 --- a/agent/tests.py +++ /dev/null @@ -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') diff --git a/agent/transport/__init__.py b/agent/transport/__init__.py deleted file mode 100644 index 4bc90fb..0000000 --- a/agent/transport/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -$Id$ -""" - diff --git a/agent/transport/base.py b/agent/transport/base.py deleted file mode 100644 index ceff3dc..0000000 --- a/agent/transport/base.py +++ /dev/null @@ -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 - diff --git a/agent/transport/httpput.txt b/agent/transport/httpput.txt deleted file mode 100644 index da808a4..0000000 --- a/agent/transport/httpput.txt +++ /dev/null @@ -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() - diff --git a/agent/ui/StartUpAgentUI.tac.py b/agent/ui/StartUpAgentUI.tac.py deleted file mode 100644 index 82117ff..0000000 --- a/agent/ui/StartUpAgentUI.tac.py +++ /dev/null @@ -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) - diff --git a/agent/ui/__init__.py b/agent/ui/__init__.py deleted file mode 100644 index 4bc90fb..0000000 --- a/agent/ui/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -$Id$ -""" - diff --git a/agent/ui/joblist.txt b/agent/ui/joblist.txt deleted file mode 100644 index cab2483..0000000 --- a/agent/ui/joblist.txt +++ /dev/null @@ -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) \ No newline at end of file diff --git a/agent/ui/resources/base.css b/agent/ui/resources/base.css deleted file mode 100644 index c5608fe..0000000 --- a/agent/ui/resources/base.css +++ /dev/null @@ -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; } - diff --git a/agent/ui/resources/custom.css b/agent/ui/resources/custom.css deleted file mode 100644 index db03dc5..0000000 --- a/agent/ui/resources/custom.css +++ /dev/null @@ -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; -} - diff --git a/agent/ui/resources/favicon.png b/agent/ui/resources/favicon.png deleted file mode 100644 index 1fc7485..0000000 Binary files a/agent/ui/resources/favicon.png and /dev/null differ diff --git a/agent/ui/resources/loops.css b/agent/ui/resources/loops.css deleted file mode 100644 index c02ba75..0000000 --- a/agent/ui/resources/loops.css +++ /dev/null @@ -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; -} - diff --git a/agent/ui/resources/loops.js b/agent/ui/resources/loops.js deleted file mode 100644 index bb7751e..0000000 --- a/agent/ui/resources/loops.js +++ /dev/null @@ -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' + title + ''; - var tr = document.createElement('tr'); - tr.appendChild(td); - node.appendChild(tr); -} - diff --git a/agent/ui/resources/loops_logo.png b/agent/ui/resources/loops_logo.png deleted file mode 100644 index 15123ab..0000000 Binary files a/agent/ui/resources/loops_logo.png and /dev/null differ diff --git a/agent/ui/resources/loops_logo2.png b/agent/ui/resources/loops_logo2.png deleted file mode 100644 index 2c0af50..0000000 Binary files a/agent/ui/resources/loops_logo2.png and /dev/null differ diff --git a/agent/ui/resources/print.css b/agent/ui/resources/print.css deleted file mode 100644 index 239a028..0000000 --- a/agent/ui/resources/print.css +++ /dev/null @@ -1,13 +0,0 @@ -/* - $Id$ - -*/ - -.top, #header, #menu, #sub-section, #footer { - display: none; -} - -#content { - width: 100%; - color: Black; -} diff --git a/agent/ui/resources/zope3_tablelayout.css b/agent/ui/resources/zope3_tablelayout.css deleted file mode 100644 index 70aceb6..0000000 --- a/agent/ui/resources/zope3_tablelayout.css +++ /dev/null @@ -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; -} - diff --git a/agent/ui/templates/agent.html b/agent/ui/templates/agent.html deleted file mode 100644 index 1b32cf7..0000000 --- a/agent/ui/templates/agent.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - -
-
-
- - - - - -
-
-
- -
-
-

-
-
-

Startpage

- -
    -
  • 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 - Professional and Simple Mode. This button is to be found on this start page only.
  • -
-
-
-

Version

-
    -
  • Agent:
  • -
-
-
-
- -
-
- -
-
- -
- - - - \ No newline at end of file diff --git a/agent/ui/templates/filecrawl.html b/agent/ui/templates/filecrawl.html deleted file mode 100644 index 8e6e82c..0000000 --- a/agent/ui/templates/filecrawl.html +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - - - - -
-
-
- - - -
-
-
- -
-
-

-
-
-

Agent: Create Filesystem Crawler Job

- -
    -
  • Configuration page for Filesystem Crawler Jobs
  • -
-
-
-

Overview

-
    -
  • File Collection
  • -
-
- -

-

-

-
-
-
- - Filesystem Crawl Settings - -
- Directories to crawl - - - - - - - - - -
- -

- please use ';' as delimiters -

-

- you can also use patterns like regular expressions -

-
- -
- -

- please use ';' as delimiters -

-

- you can also use patterns like regular expressions -

-
- -
-
-
- - Filter criteria patterns - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - -
- - - -
- -

- no size means that transferred files might be very large! -

-
- -
- - - -
- - - -
- - - -
- - - -
-
-
- - Job Interval - - - - - - - - - - -
- - - -
- - - -
-
- -
-
-
-
-
-
-
- -
-
- -
-
- -
- - - - \ No newline at end of file diff --git a/agent/ui/templates/footer.html b/agent/ui/templates/footer.html deleted file mode 100644 index 368fbcb..0000000 --- a/agent/ui/templates/footer.html +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/agent/ui/templates/header.html b/agent/ui/templates/header.html deleted file mode 100644 index a3c209d..0000000 --- a/agent/ui/templates/header.html +++ /dev/null @@ -1,22 +0,0 @@ - - loops Agent - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/agent/ui/templates/jobdetail.html b/agent/ui/templates/jobdetail.html deleted file mode 100644 index 70c298e..0000000 --- a/agent/ui/templates/jobdetail.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - -
-
-
- - - -
-
-
- -
-
-

-
-
-

Agent Job overview

- -
    -
  • Here you will get an overview of the current jobs registered in the agent system
  • -
-
-
-

Overview

- -
-
-
- -
-
- -
-
- -
- - - - \ No newline at end of file diff --git a/agent/ui/templates/joblisting.html b/agent/ui/templates/joblisting.html deleted file mode 100644 index cf86cd7..0000000 --- a/agent/ui/templates/joblisting.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - -
-
-
- - - -
-
-
- -
-
-

-
-
-

Agent Job overview

- -
    -
  • Here you will get an overview of the current jobs registered in the agent system
  • -
-
-
-

Overview

-
    -
  • Current jobs
  • - - - - - - - - - - - -

    -

    - -
    - PID - - State - - Interval - - Search Criteria - - Job Scope -
    -
-
-
-
- -
-
- -
-
- -
- - - - \ No newline at end of file diff --git a/agent/ui/templates/mail_detailed.html b/agent/ui/templates/mail_detailed.html deleted file mode 100644 index 482222d..0000000 --- a/agent/ui/templates/mail_detailed.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - -
-
-
- - - -
-
-
- -
-
-

-
-
-

Agent: collected Outlook Mails

- -
    -
  • All currently available Outlook Mails collected by the loops agent
  • -
-
-
-

Overview

-
    -
  • Mail in Detail
  • -
-
-
- - - - - - - - -

-

- -
- Field - - Value -
-
-
- [back to Ressources overview] -
-
-
- -
-
- -
-
- -
- - - - \ No newline at end of file diff --git a/agent/ui/templates/mailcrawl.html b/agent/ui/templates/mailcrawl.html deleted file mode 100644 index c7201a9..0000000 --- a/agent/ui/templates/mailcrawl.html +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - - - - -
-
-
- - - -
-
-
- -
-
-

-
-
-

Agent: Create Mail Crawl Job

- -
    -
  • Configuration page for Outlook Mail Crawler jobs.
  • -
-
-
-

Overview

-
    -
  • Mail Collection
  • -
-
- -

-

-

-
-
-
- - Mail Crawl Settings - -
- Folders to crawl - - - - - - - - - - - - - -
- - - -
- - - -
- - - -
-
-
- - Filter criteria patterns - - - - - - - - - - -
- - - -
- - - -
-
-
- - Message parts to be stored - - - - - - - - - - -
- - - -
- - - -
-
-
- - Job Interval - - - - - - - - - - -
- - - -
- - - -
-
- -
-
-
-
-
-
-
- -
-
- -
-
- -
- - - - \ No newline at end of file diff --git a/agent/ui/templates/navigation.html b/agent/ui/templates/navigation.html deleted file mode 100644 index 5b899de..0000000 --- a/agent/ui/templates/navigation.html +++ /dev/null @@ -1,41 +0,0 @@ -
-

Navigation

- -
\ No newline at end of file diff --git a/agent/ui/templates/ressourceview.html b/agent/ui/templates/ressourceview.html deleted file mode 100644 index 0de0f0a..0000000 --- a/agent/ui/templates/ressourceview.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - -
-
-
- - - -
-
-
- -
-
-

-
-
-

Agent: collected ressources

- -
    -
  • All currently available objects that were collected by loops jobs
  • -
-
-
-

Overview

-
    -
  • Ressource Collection
  • -
-
-
- - -

-

- - -

-

- -
-
-
-
-
- -
-
- -
-
- -
- - - - \ No newline at end of file diff --git a/agent/ui/templates/top.html b/agent/ui/templates/top.html deleted file mode 100644 index b7fe491..0000000 --- a/agent/ui/templates/top.html +++ /dev/null @@ -1,5 +0,0 @@ -
-
- -
-
\ No newline at end of file diff --git a/agent/ui/twistd.py b/agent/ui/twistd.py deleted file mode 100644 index 3be2802..0000000 --- a/agent/ui/twistd.py +++ /dev/null @@ -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() diff --git a/agent/ui/usermode.ini b/agent/ui/usermode.ini deleted file mode 100644 index c6aee86..0000000 --- a/agent/ui/usermode.ini +++ /dev/null @@ -1 +0,0 @@ -UserMode:Simple \ No newline at end of file diff --git a/agent/ui/web.py b/agent/ui/web.py deleted file mode 100644 index a4a938d..0000000 --- a/agent/ui/web.py +++ /dev/null @@ -1,1059 +0,0 @@ -#------------------------------------------------------ -# AgentHome.py -# source file for agent UI -# author: Juergen Menzinger -# version: 0.1 -#------------------------------------------------------ - -""" - -$Id$ -""" - -import sys -import os -import tempfile -import cPickle -import traceback -import time -import email -try: - import _winreg as winreg - USE_WINDOWS = True -except ImportError: - USE_WINDOWS = False - -from nevow import loaders, rend, static, url, inevow, tags -from nevow.inevow import IRequest -from twisted.internet import defer - -from loops.agent import core -if USE_WINDOWS: - from loops.agent.crawl import outlook - from loops.agent.crawl.outlook import OutlookResource -from loops.agent.crawl.filesystem import FileResource - -# ---- global definitions and settings --------------------------------- -os.environ['HOME'] = os.path.dirname(core.__file__) -OUTLOOK2000 = 1 -PICKLE_MAILS = 0 -DEBUG = 1 -resourcesDirectory = 'resources' -templatesDirectory = 'templates' - -# some constatnts -_REG_KEY_CONST_ = "SOFTWARE\\Classes\\mailto\\shell\\open\\command" -_OUTLOOK_2007_ = "Office12" -_OUTLOOK_2003_ = "Office11" -_OUTLOOK_2000_ = "Office10" -_OUTLOOK_EXPRESS_ = "Office10" -#---------------------------------------------------------- - - -def template(fn): - return loaders.xmlfile(os.path.join( - os.path.dirname(__file__), templatesDirectory, fn)) - - -def getConfigIndex(agentobj, index_name, default_value, fn=None): - """get the number of jobs currently in config file""" - index = len(agentobj.config.crawl) - return index - - -def getOutlookVersion(): - """ - checks what outlook version we have to handle - - Returns the standard email application on this machine. - - Therefor we have to read out the registry key - "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\mailto\shell\open\command" - - --ATTENTION--: - This was tested with Outlook 2007 only. - Therefor it is possible that there are problems - with some constants defined on the beginning. - - TODO: -> check this function with some other - Outlook versions installed and modify, - if needed the defined constants and return - values of the getOutlookVersion function - - """ - # open registry key - try: - key = winreg.OpenKey( - winreg.HKEY_LOCAL_MACHINE, - _REG_KEY_CONST_ ) - - except WindowsError: - return None - - try: - try: - # read out current standard outlook version - version = winreg.QueryValueEx(key, "")[0] - # check what outlook we have - if version: - if _OUTLOOK_2007_ in version: - version = _OUTLOOK_2007_ - - elif _OUTLOOK_2003_ in version: - version = _OUTLOOK_2003_ - - elif _OUTLOOK_EXPRESS_ in version: - version = _OUTLOOK_EXPRESS_ - - except WindowsError: - version = "" - finally: - # close key - winreg.CloseKey(key) - - #print '--> getOutlookVersion(): Outlook version found, version is: ', version - # return key value - return version - - -# AgentHome -# root page of the Agent UI - -class AgentHome(rend.Page): - - """Main class that builds the root ressource for the loops agent ui. - - Instance variables: - usermode -- can be 'Simple' or 'Advanced' - agent -- agent object where config and tempdir attributes can be accessed - - Class methods: - At the moment all methods of this class except the __init__ although - public, are intended to be called directly or stand-alone. - - __init__ -- Load the initial start-up settings from config - - """ - - child_resources = static.File(os.path.join( - os.path.dirname(__file__),resourcesDirectory)) - docFactory = template('agent.html') - - def __init__(self, agent=None, first_start=1): - """ Initialize the AgentHome object. - - If first_start = 1 then the initalization procedure is run, which - mainly loads the stored settings from the agent ini-file into instance - variables of the class. - - Keyword arguments: - first_start -- defaults to 1 - - """ - print "[PAGECLASS] AgentHome" - if first_start == 1: - self.agent = core.Agent() - self.usermode = self.agent.config.ui.web.setdefault('usermode', 'Simple') - print "[AgentHome] setting self.usermode: ", self.agent.config.ui.web.usermode - else: - self.agent = agent - - """ - def locateChild(self, ctx, segments): - return self, () - """ - - # calls to child pages - - def child_joboverview(self, context): - """ User requested page from menue: 'job overview' """ - return JobOverView(self.agent) - - def child_collectOutlookMails(self, context): - """Display page for starting Outlook Crawler""" - return AgentOutlookMailCrawl(self.agent) - - def child_collectFilesystem(self, context): - """Display page for starting Filesystem Crawler""" - return AgentFilesystemCrawl(self.agent) - - def child_viewRessources(self, context): - """Display page that shows all currently collected Outlook Mails.""" - return RessourceView(self.agent) - - # "Startpage" methods (class AgentHome) - - def child_changeUserMode(self, context): - """Change user mode. - - User has klicked the Change User Mode button, so - change UserMode from Simple <-> Professional - - """ - if DEBUG: - print "[child_changeUserMode] UserMode: ", self.agent.config.ui.web.usermode - form = IRequest(context).args - if form != {}: - for elem in form: - print "[child_changeUserMode] ", form[elem] - if self.agent.config.ui.web.usermode == "Simple": - self.agent.config.ui.web.usermode = "Advanced" - else: - self.agent.config.ui.web.usermode = "Simple" - self.agent.config.save() - if DEBUG: - print "====> changed UserMode: ", self.agent.config.ui.web.usermode - return AgentHome(self.agent, first_start=0) - - # "job overview" methods (class JobOverView) - - def child_ViewJobDetails(self, context): - """Get details for the selected job. - - Reads all information about the selected job from file/ database. - Returns page object which displays the available information. - - """ - selected_job = ((IRequest(context).uri).split("?"))[1] - crawl_index = selected_job.split(".")[1] - - return JobOverViewDetails(self.agent, crawl_index) - - # "add outlook crawl job" methods (class AgentOutlookMailCrawl) - - def child_submitOutlookCrawlJob(self,context): - """Initiate Outlook crawling job as requested by user.""" - ag = self.agent - conf = ag.config - crawlSubfolder = False - form = IRequest(context).args - if form != {}: - index = getConfigIndex(self.agent, 'type', 'unused') - #save job configuration - if form['mailCrawlInterval'][0] == "oneTime": - conf.crawl[index].state = 'completed' - else: - conf.crawl[index].state = 'active' - conf.crawl[index].jobid = 'outlook.%i' %(index) - conf.crawl[index].type = 'OutlookMail' - if form.has_key('inbox'): - conf.crawl[index].getinbox = '%s'%(str(form["inbox"][0])) - else: - conf.crawl[index].getinbox = 'False' - if form.has_key('subfolders'): - conf.crawl[index].getsubfolder = '%s'%(str(form["subfolders"][0])) - else: - conf.crawl[index].getsubfolder = 'False' - conf.crawl[index].subfolderpat = '%s'%(str(form["pattern"][0])) - conf.crawl[index].latest = '%i'%(index) - #TODO: how to find out what the most actual element is? - conf.crawl[index].filter_criteria = '%s'%(form["selectFilterCriteria"][0]) - conf.crawl[index].filter_pattern = '%s'%(form["filterPattern"][0]) - conf.crawl[index].content_format = '%s'%(form["selectContentFormat"][0]) - conf.crawl[index].include_attachements = '%s'%(form["selectAttachements"][0]) - conf.crawl[index].interval = '%s'%(form["mailCrawlInterval"][0]) - conf.save() - if form["mailCrawlInterval"][0] == 'oneTime': - # get version of standard mail client - outlookVersion = getOutlookVersion() - # set properties depend on version - if outlookVersion == _OUTLOOK_2000_: - fieldsMail = ['Body', - 'HTMLBody', - 'CC', - 'SenderName', - 'Recipients', - 'To', - 'Attachments', - 'Subject' - ] - elif outlookVersion == _OUTLOOK_2007_: - fieldsMail = ['Body', - 'BodyFormat', - 'HTMLBody', - 'CC', - 'SenderEmailAddress', - 'Recipients', - 'To', - 'Attachments', - 'Subject', - 'ReceivedTime' - ] - - # all form fileds in the html template are named according to - # the dictionary name if expected to be in params, - # this way it is not necessary to alter the code if another - # parameter is added - params = {} - for elem in form.items(): - params[elem[0]] = elem[1][0] - outlook_job = outlook.CrawlingJob() - outlook_job.params = params - deferred = outlook_job.collect() - deferred.addCallback(self.defMailCrawl, index, params, context) - deferred.addErrback(self.defMailCrawlError,context) - return deferred - else: - #TODO implement forwarding to next form (scheduler) - return AgentOutlookMailCrawl(self.agent, "Scheduled Mail Crawling not implemented yet.") - - else: - return AgentOutlookMailCrawl(self.agent, "An error occurred: form data has been empty.") - return AgentOutlookMailCrawl(self.agent, "Crawl Job settings have been saved.") - - def defMailCrawl(self, mail_collection, index, params, context): - """Save and Forward the mail collection to a page view.""" - if DEBUG: - print "====> seting and saving mails to disk" - print "====> agent base dir: ",self.agent.tempdir - tmpdir = tempfile.mkdtemp(prefix='mailJob_%i_'%(index), dir=self.agent.tempdir) - if DEBUG: - print "====> outlook job dir: ", tmpdir - files = os.listdir(tmpdir) - filenum = len(files) - for elem in mail_collection: - tmpfile = tempfile.mkstemp(prefix='%i_mail_'%(filenum), dir=tmpdir) - if PICKLE_MAILS == 0: - # write data as MIME string into a text file - os.write(tmpfile[0], elem.data.as_string()) - else: - # alternatively also writing the list as a whole might be - # more performant (less disc I/O) but how to store the - # path so that the user can have a look at the mail view - # page and the page knows in which directory to look? - # Idea could be a object that stores all current paths and - # their crawlIDs - os.write(tmpfile[0], cPickle.dumps(elem)) - os.close(tmpfile[0]) - filenum = filenum + 1 - return RessourceView(self.agent, mail_collection, "The collected mails have been saved in %s"%(tmpdir), tmpdir) - - def defMailCrawlError(self, ErrorMessage, context): - """Handles errors that ocurred in the MailCrawler.""" - #TODO: handle whole exception information - return traceback.format_exc() - - # "view collected ressources" methods (class RessourceViewView) - - def child_viewRessourceDetails(self,context): - """Get details for the selected ressource object. - - Reads all information about the selected ressource from file/ or the - mail_collection list. - Returns page object which displays the available information. - - """ - selected_item = ((IRequest(context).uri).split("?"))[1] - form = IRequest(context).args - if form != {}: - requested_file="" - if DEBUG: - print "[child_viewRessourceDetails]" - print "====> selected item: ", selected_item - print "====> printing items in directory" - fileslist = os.listdir(form['ressourceObjData'][0]) - for elem in fileslist: - if DEBUG: - print elem - if elem.startswith(("%i_mail_"%(int(selected_item))), 0): - requested_file = elem - requested_file = os.path.join(form['ressourceObjData'][0], requested_file) - if requested_file.find("mail") > 0: - fp = open(requested_file, "rb") - mail_parser = email.Parser.Parser() - mailobj = OutlookResource(mail_parser.parse(fp)) - fp.close() - mail = [] - for elem in mailobj.data.items(): - if DEBUG: - print "====> building MIMEObject" - print "====> appending attribute: ", elem - mail.append(elem) - if hasattr(mailobj.data, 'preamble'): - if DEBUG: - print "====> copying preamble contents" - mail.append(['Preamble', mailobj.data.preamble]) - else: - mail.append(['Preamble','']) - if hasattr(mailobj.data,'epilogue'): - if DEBUG: - print "====> copying epilogue contents" - mail.append(['Epilogue', mailobj.data.epilogue]) - else: - mail.append(['Epilogue', '']) - return OutlookMailDetail(agent=self.agent, pagemessage="", mail=mail, filename=requested_file) - elif requested_file.find("file") > 0: - #TODO implement file analyzing - return FileObjectDetail(agent=self.agent, pagemessage="", fileobj=fileobj, filename=requested_file) - if os.path.isdir(selected_item): - # selected item is a folder -> change to that folder - if DEBUG: - print "====> selected item is a directory! changing into directory" - return RessourceView(self.agent, ressource_collection=[], pagemessage="changed to folder %s" %(selected_item), tmpdir=selected_item) - elif os.path.isfile(selected_item): - if selected_item.find("mail") > 0: - fp = open(selected_item, "rb") - mail_parser = email.Parser.Parser() - mailobj = OutlookResource(mail_parser.parse(fp)) - fp.close() - mail = [] - for elem in mailobj.data.items(): - mail.append(elem) - if hasattr(mailobj.data,'preamble'): - mail.append(['Preamble', mailobj.data.preamble]) - else: - mail.append(['Preamble','']) - if hasattr(mailobj.data,'epilogue'): - mail.append(['Epilogue',mailobj.data.epilogue]) - else: - mail.append(['Epilogue','']) - requested_file = selected_item - return OutlookMailDetail(agent=self.agent, pagemessage="", mail=mail, filename=requested_file) - elif selected_item.find("file") > 0: - #TODO implement file analyzing - return FileObjectDetail(agent=self.agent, pagemessage="", fileobj=fileobj, filename=requested_file) - - # rendering methods of Startpage - - def render_getActiveUserMode(self, context, data): - return self.agent.config.ui.web.usermode - - def render_getAgentVersion(self, context, data): - return "0.1 alpha" - - def render_footer_fragment(self, context, data): - return context.tag[FooterFragment(data)] - - def render_navigation_fragment(self, context, data): - return context.tag[NavigationFragment(data)] - - def render_top_fragment(self, context, data): - return context.tag[TopFragment(data)] - - def render_header_fragment(self, context, data): - return context.tag[HeaderFragment(data)] - - -class FooterFragment(rend.Fragment): - docFactory = template('footer.html') - - -class NavigationFragment(rend.Fragment): - docFactory = template('navigation.html') - - -class TopFragment(rend.Fragment): - docFactory = template('top.html') - - -class HeaderFragment(rend.Fragment): - docFactory = template('header.html') - - -# subpages of AgentHome - -class JobOverView(rend.Page): - - """Builds page that lists all currently registered jobs. - - Instance variables: - agent -- agent object where config and tempdir attributes can be accessed - - Class methods: - At the moment all methods of this class except the __init__ - although public, are intended to be called directly or stand-alone. - - __init__ -- Store the initial settings retrieved from AgentHome. - - """ - - docFactory = template('joblisting.html') - - def __init__(self, agent=None): - print "[PAGECLASS] JobOverView" - self.agent = agent - - # rendering methods of job overview - - def data_displayViewForm(self, context, data): - return "Overview of all running Crawling jobs" - - def render_getActiveUserMode(self, context, data): - return self.agent.config.ui.web.usermode - - def render_fillJobTable(self, ctx, data): - """Build table which displays all registered jobs.""" - - #---- get the registered jobs from the jobfile ---- - joblist = [] - index = 0 - self.agent.config.load() - while index < len(self.agent.config.crawl): - joblist.append(dict(self.agent.config.crawl[index].items())) - index = index + 1 - job_table = [] - for job_details in joblist: - if job_details['jobid'].split(".")[0] == "outlook": - job_table.append(tags.tr - [ - tags.td - [ - tags.a(href="ViewJobDetails?%s" %(job_details['jobid'])) - [ - tags.b - [ - "[" + job_details['jobid'] +"]" - ] - ] - ], - tags.td[job_details['state']], - tags.td[job_details['interval']], - tags.td[job_details['filter_criteria']], - tags.td["Inbox: %s, Subfolders: %s" %(job_details['getinbox'], job_details['getsubfolder'])] - ] - ) - elif job_details['jobid'].split(".")[0] == "filesystem": - job_table.append(tags.tr - [ - tags.td - [ - tags.a(href="ViewJobDetails?%s" %(job_details['jobid'])) - [ - tags.b - [ - "[" + job_details['jobid'] +"]" - ] - ] - ], - tags.td[job_details['state']], - tags.td[job_details['interval']], - tags.td[job_details['filter_criteria']], - tags.td["directories"] - ] - ) - - return job_table - - def render_footer_fragment(self, context, data): - return context.tag[FooterFragment(data)] - - def render_navigation_fragment(self, context, data): - return context.tag[NavigationFragment(data)] - - def render_top_fragment(self, context, data): - return context.tag[TopFragment(data)] - - def render_header_fragment(self, context, data): - return context.tag[HeaderFragment(data)] - - -class JobOverViewDetails(rend.Page): - - """Builds page that displays detailed information about a selected job. - - Instance variables: - jobdetails -- list that contains all available job information - agent -- agent object where config and tempdir attributes can be accessed - - Class methods: - At the moment all methods of this class except the __init__ although public, - are intended to be called directly or stand-alone. - - __init__ -- Store the selected job in member variable. - - """ - - docFactory = template('jobdetail.html') - - def __init__(self, agent=None, selected_jobindex=None): - print "[PAGECLASS] JobOverViewDetails" - self.selected_index = selected_jobindex - self.agent = agent - - # rendering methods of job view details - - def data_displayViewForm(self, context, data): - return "Detailed view of crawling job." - - def render_getActiveUserMode(self, context, data): - return self.agent.config.ui.web.usermode - - def render_displayJobDetails(self, ctx, data): - """Build the table containing information about the selected job.""" - - print "*******************************************************" - print "[render_displayJobDetails] received form: ", str(self.selected_index) - print "[render_displayJobDetails] usermode: ", self.agent.config.ui.web.usermode - print "*******************************************************" - - if self.selected_index != None: - job_detailtable = [tags.tr - [ - tags.td[tags.b[parameter[0]]], - tags.td[parameter[1]] - ] - for parameter in list(self.agent.config.crawl[int(self.selected_index)].items()) - ] - return job_detailtable - - def render_footer_fragment(self, context, data): - return context.tag[FooterFragment(data)] - - def render_navigation_fragment(self, context, data): - return context.tag[NavigationFragment(data)] - - def render_top_fragment(self, context, data): - return context.tag[TopFragment(data)] - - def render_header_fragment(self, context, data): - return context.tag[HeaderFragment(data)] - - -class RessourceView(rend.Page): - - """Builds page that displays an overview of all collected mails. - - Instance variables: - page_message -- string that is displayed on the page - ressource_collection -- collection containing all currently collected ressources. - temp_dir -- actual directory whose ressources have to be displayed - agent -- the agent object which holds the config data and temp directory - - Class methods: - At the moment all methods of this class except the __init__ although public, - are intended to be called directly or stand-alone. - - __init__ -- Store the mail collection object and the pagemessage - - """ - - mail_collection = None - docFactory = template('ressourceview.html') - - def __init__(self, agent=None, ressource_collection=[], pagemessage="", tmpdir=""): - print "[PAGECLASS] RessourceView" - self.ressource_collection = ressource_collection - self.page_message = pagemessage - self.temp_dir = tmpdir - self.agent = agent - - # rendering methods of view collected ressources - - def data_displayViewForm(self, context, data): - return "View of all collected ressources." - - def render_getActiveUserMode(self, context, data): - return self.agent.config.ui.web.usermode - - def render_systemMessage(self, context, data): - """Displays messages from the system to the user.""" - message = tags.b[self.page_message] - return message - - def render_displayRessourceHeaders(self, context, data): - """Displays the column headings according to the ressource type""" - #TODO: switch between different objecttypes - if DEBUG: - print "====> creating html columns for Ressource table" - current_dir="" - if self.temp_dir == "": - current_dir = self.agent.tempdir - else: - current_dir = self.temp_dir - if self.ressource_collection == []: - files_found = 0 - for elem in os.listdir(current_dir): - if os.path.isfile(os.path.join(current_dir,elem)): - files_found = 1 - break - if files_found == 0: - if current_dir.find("mail"): - if DEBUG: - print "====> just mail subdirs in current directory" - return [tags.tr - [ - tags.th["Directory Name"], - tags.th["Created"], - tags.th["Modified"], - tags.th["Mails inside"], - ] - ] - elif current_dir.find("file"): - if DEBUG: - print "====> just file subdirs in current directory" - return [tags.tr - [ - tags.th["Directory Name"], - tags.th["Created"], - tags.th["Modified"], - tags.th["Files inside"], - ] - ] - if files_found >= 1: - if current_dir.find("mail"): - if DEBUG: - print "====> current directory has mails" - return [tags.tr - [ - tags.th["From"], - tags.th["CC"], - tags.th["Subject"], - tags.th["Recipient"], - tags.th["Date"] - ] - ] - elif current_dir.find("file"): - if DEBUG: - print "====> current directory has files" - return [tags.tr - [ - tags.th["Filename"], - tags.th["Filesize"], - tags.th["Date created"], - tags.th["Date modified"], - tags.th["Filetype"] - ] - ] - elif isinstance(self.ressource_collection[0], FileResource): - if DEBUG: - print "====> instance is a FileResource" - return [tags.tr - [ - tags.th["Filename"], - tags.th["Filesize"], - tags.th["Date created"], - tags.th["Date modified"], - tags.th["Filetype"] - ] - ] - elif isinstance(self.ressource_collection[0], OutlookResource): - if DEBUG: - print "====> instance is a OutlookResource" - return [tags.tr - [ - tags.th["From"], - tags.th["CC"], - tags.th["Subject"], - tags.th["Recipient"], - tags.th["Date"] - ] - ] - # raise exception here? - return "could not find a matching object type" - - def render_displayRessources(self, ctx, data): - """Builds table containing all currently collected ressources.""" - if self.ressource_collection != []: - if DEBUG: - print "====> building ressource with ressource_collection" - index = 0 - ressource_table = [] - if isinstance(self.ressource_collection[0],OutlookResource): - for ressourceObject in self.ressource_collection: - ressource_table.append(tags.form(name="ressourceEntry%i"%(index), action="viewRessourceDetails?%i"%(index), method="POST")[ - tags.input(name="ressourceObjData", type="hidden", value="%s"%(self.temp_dir)), - tags.tr[ - tags.td[ - tags.a(href="javascript:document.getElementsByName('ressourceEntry%i')[0].submit()"%(index))[ - tags.b[ - "[" + str(ressourceObject.data.get('From')) +"]" #SenderName - ] - ] - ], - tags.td[ - str(ressourceObject.data.get('CC')) - ], - tags.td[ - str(ressourceObject.data.get('Subject')) - ], - tags.td[ - #TODO: proper handling of unicode characters - ressourceObject.data.get('To').encode('utf-8') - ], - tags.td['SourceFolder']]]) - index = index + 1 - return ressource_table - - if isinstance(self.ressource_collection[0],FileResource): - #TODO: implement building the table for file objects - return ressource_table - - else: - if DEBUG: - print "====> building ressource by analyzing submitted dir" - if self.temp_dir == "": - current_dir = self.agent.tempdir - else: - current_dir = self.temp_dir - ressource_files= os.listdir(current_dir) - ressource_table = [] - #check whether there are directories, files or both in the actual directory - for elem in ressource_files: - element = os.path.join(current_dir,elem) - if os.path.isdir(element): - files_in_subdir = len(os.listdir(element)) - ressource_table.append(tags.tr - [ - tags.td - [ - tags.a(href="viewRessourceDetails?%s" %(element)) - [ - tags.b - [ - "[" + str(elem) +"]" - ] - ] - ], - tags.td[time.asctime(time.localtime(os.path.getctime(element)))], - tags.td[time.asctime(time.localtime(os.path.getmtime(element)))], - tags.td[files_in_subdir] - ] - ) - - elif os.path.isfile(element): - if elem.find("file") > 0: - if DEBUG: - print "====> %s is of type crawl file" %(elem) - ressource_table.append(tags.tr - [ - tags.td - [ - tags.a(href="viewRessourceDetails?%s" %(element)) - [ - tags.b - [ - "[" + str(elem) +"]" - ] - ] - ], - tags.td[os.path.getsize(element)], - tags.td[time.asctime(time.localtime(os.path.getctime(element)))], - tags.td[time.asctime(time.localtime(os.path.getmtime(element)))], - tags.td[str(elem.split(".")[-1])] - ] - ) - elif elem.find("mail") > 0: - if DEBUG: - print "====> %s is of type mail" %(elem) - fp = open(element,"rb") - mail_parser = email.Parser.Parser() - mail = OutlookResource(mail_parser.parse(fp)) - fp.close() - mail_from = mail.data.get('From', "not available") - mail_cc = mail.data.get('CC', "not available") - mail_subject = mail.data.get('Subject', "not available") - mail_recipient = mail.data.get('TO', "not available") - mail_date = mail.data.get('SENT', "not available") - ressource_table.append(tags.tr - [ - tags.td - [ - tags.a(href="viewRessourceDetails?%s" %(element)) - [ - tags.b - [ - "[" + mail_from +"]" - ] - ] - ], - tags.td[mail_cc], - tags.td[mail_subject], - tags.td[mail_recipient], - tags.td[mail_date] - ] - ) - return ressource_table - - def render_footer_fragment(self, context, data): - return context.tag[FooterFragment(data)] - - def render_navigation_fragment(self, context, data): - return context.tag[NavigationFragment(data)] - - def render_top_fragment(self, context, data): - return context.tag[TopFragment(data)] - - def render_header_fragment(self, context, data): - return context.tag[HeaderFragment(data)] - - -class AgentOutlookMailCrawl(rend.Page): - - """Builds page where an Outlook Mail Crawler can be configured and run. - - Instance variables: - page_message -- string that is displayed on the page - - Class methods: - At the moment all methods of this class except the __init__ although public, - are intended to be called directly or stand-alone. - - __init__ -- store pagemessage in member variable - - """ - - docFactory = template('mailcrawl.html') - - def __init__(self, agent=None, pagemessage=""): - print "[PAGECLASS] AgentOutlookMailCrawl" - self.page_message = pagemessage - self.agent = agent - - # rendering methods of add outlook crawl job - - def data_displayViewForm(self, context, data): - return "Configure your MailCrawl Job in the form below.\ - You then can either choose to start it as a\ - single job, or as scheduled job (further input\ - for scheduler necessary). For each new collect\ - procedure a filter will be added, so that just\ - mails received in Outlook after the recent Crawl\ - Job has been run, will be collected." - - def render_getActiveUserMode(self, context, data): - return self.agent.config.ui.web.usermode - - def render_displayOutlookMails(self, ctx, data): - """Currently no implementation""" - return "" - - def render_systemMessage(self, context, data): - """Displays messages from the system to the user.""" - message = tags.b[self.page_message] - return message - - def render_footer_fragment(self, context, data): - return context.tag[FooterFragment(data)] - - def render_navigation_fragment(self, context, data): - return context.tag[NavigationFragment(data)] - - def render_top_fragment(self, context, data): - return context.tag[TopFragment(data)] - - def render_header_fragment(self, context, data): - return context.tag[HeaderFragment(data)] - - -class AgentFilesystemCrawl(rend.Page): - - """Builds page where an Filesystem Crawler can be configured and run. - - Instance variables: - page_message -- string that is displayed on the page - - Class methods: - At the moment all methods of this class except the __init__ although public, - are intended to be called directly or stand-alone. - - __init__ -- store pagemessage in member variable - - """ - - docFactory = template('filecrawl.html') - - def __init__(self, agent=None, pagemessage=""): - print "[PAGECLASS] AgentFilesystemCrawl" - self.page_message = pagemessage - self.agent = agent - - # rendering methods of ad filesystem crawl job - - def data_displayViewForm(self, context, data): - return "Configure your FileCrawl Job in the form below.\ - You then can either choose to start it as a\ - single job, or as scheduled job (further input\ - for scheduler necessary). For each new collect\ - procedure a filter will be applied, so that just\ - new objects will be collected." - - def render_getActiveUserMode(self, context, data): - return self.agent.config.ui.web.usermode - - def render_displayFiles(self, ctx, data): - """Currently no implementation""" - return "" - - def render_systemMessage(self, context, data): - """Displays messages from the system to the user.""" - message = tags.b[self.page_message] - return message - - def render_footer_fragment(self, context, data): - return context.tag[FooterFragment(data)] - - def render_navigation_fragment(self, context, data): - return context.tag[NavigationFragment(data)] - - def render_top_fragment(self, context, data): - return context.tag[TopFragment(data)] - - def render_header_fragment(self, context, data): - return context.tag[HeaderFragment(data)] - - -class OutlookMailDetail(rend.Page): - - """Builds page that displays the selected mail in detail. - - Instance variables: - page_message -- string that is displayed on the page - mail_elements -- received mailobject as a list. - agent -- agent object that holds the config data - mail_file -- filename of the selected mail to view - - Class methods: - At the moment all methods of this class except the __init__ although public, - are intended to be called directly or stand-alone. - - __init__ -- Store the mail collection object and the pagemessage - - """ - - docFactory = template('mail_detailed.html') - - def __init__(self, agent=None, pagemessage="", mail=[], filename=""): - print "[PAGECLASS] OutlookMailDetail" - self.mail_elements = mail - self.page_message = pagemessage - self.agent = agent - self.mail_file = filename - - # rendering methods of Collect Outlook Mails - - def data_displayViewForm(self, context, data): - return (tags.b["Filename of the selected mail: "], tags.i[self.mail_file]) - - def render_getActiveUserMode(self, context, data): - return self.agent.config.ui.web.usermode - - def render_systemMessage(self, context, data): - """Displays messages from the system to the user.""" - message = tags.b[self.page_message] - return message - - def render_displayOutlookMail(self, ctx, data): - """Builds the layout for the mail.""" - - if self.mail_elements != []: - mail_view = [] - for elem in self.mail_elements: - mail_view.append(tags.tr[ - tags.td[elem[0]], - tags.td[elem[1]] - ] - ) - return mail_view - - def render_footer_fragment(self, context, data): - return context.tag[FooterFragment(data)] - - def render_navigation_fragment(self, context, data): - return context.tag[NavigationFragment(data)] - - def render_top_fragment(self, context, data): - return context.tag[TopFragment(data)] - - def render_header_fragment(self, context, data): - return context.tag[HeaderFragment(data)] -