Subversion Repositories Scribus

Compare Revisions

Regard whitespace Rev 12489 → Rev 12490

/tools/mrscribe/Mantis/plugin.py.bak
0,0 → 1,201
###
# Copyright (c) 2006, 2007, Oleksandr Moskalenko
# All rights reserved.
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
###
 
import supybot.conf as conf
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.ircmsgs as ircmsgs
import supybot.callbacks as callbacks
import urllib2
import re
from BeautifulSoup import BeautifulSoup
#from BeautifulSoup import Null
 
class Utils:
"""This class contains utils used by several commands."""
def __init__(self):
pass
def _makeurl(self, server, bugno):
self.bugnumber = str(bugno)
self.server = server
self.url = 'http://'+self.server+'/view.php?id='+self.bugnumber
return self.url
def _bugdata(self, url):
self.url = url
try:
self.srchtml = urllib2.urlopen(self.url).read()
except urllib2.URLError:
print "Cannot fetch the bug page."
self.soup = BeautifulSoup(self.srchtml)
return self.soup
 
class Mantis(callbacks.PluginRegexp):
"""This plugin is non-interactive. No help is needed."""
regexps = ['bugUrl'] #,'titleSnarfer']
def __init__(self, irc):
self.__parent = super(Mantis, self)
self.__parent.__init__(irc)
if self.checkbts() == True:
self.btsstatus = 'Mantis BTS is accessible'
else:
self.btsstatus = 'Cannot access the BTS front page'
self.mantisutils = Utils()
 
def bugtitle(self, irc, msg, args, number):
"""<number>
 
Returns the Title of the bug number <number>. Only positive integers
are accepted as bug numbers.
"""
self.bugno = number
self.url = self.mantisutils._makeurl(self.server, self.bugno)
try:
self.soup = self.mantisutils._bugdata(self.url)
self.title = self.soup.html.head.title.contents
if self.title[0] == 'Mantis Issue Tracker for Scribus':
irc.reply("Sorry, it appears that this bug doesn't exist")
elif self.title == None:
irc.reply("Sorry, this bug is not accessible")
else:
self.shorttitle = self.title[0].strip().split(" - Mantis Issue Tracker")[0]
irc.reply(self.shorttitle)
except:
irc.reply("Sorry, an unknown error has occured. I'll go cry for a while")
 
bugtitle = wrap(bugtitle, ['int'])
 
def bugstatus(self, irc, msg, args, number):
"""<number>
 
Returns the status of the bug number <number>. Only positive integers
are accepted as bug numbers.
"""
self.bugno = number
self.url = self.mantisutils._makeurl(self.server, self.bugno)
try:
self.soup = self.mantisutils._bugdata(self.url)
self.title = self.soup.html.head.title.contents
if self.title[0] == 'Mantis Issue Tracker for Scribus':
irc.reply("Sorry, it appears that this bug doesn't exist")
elif self.title == None:
irc.reply("Sorry, this bug is not accessible")
else:
self.status = self.soup.firstText(re.compile('--\ Status')).findNext('td').findNext('td').contents[0].strip()
irc.reply(self.status)
except URLError, e:
irc.reply(str(e))
 
bugstatus = wrap(bugstatus, ['int'])
 
def bugreporter(self, irc, msg, args, number):
"""<number>
 
Returns the reporter of the bug number <number>. Only positive integers
are accepted as bug numbers.
"""
self.bugno = number
self.url = self.mantisutils._makeurl(self.server, self.bugno)
try:
self.soup = self.mantisutils._bugdata(self.url)
self.title = self.soup.html.head.title.contents
if self.title[0] == 'Mantis Issue Tracker for Scribus':
irc.reply("Sorry, it appears that this bug doesn't exist")
elif self.title == None:
irc.reply("Sorry, this bug is not accessible")
else:
self.reporter = self.soup.firstText(re.compile('Reporter')).findNext('td').findNext('td').contents[0].strip()
irc.reply(self.reporter)
except Error, e:
irc.reply(str(e))
 
bugreporter = wrap(bugreporter, ['int'])
 
def assignedto(self, irc, msg, args, number):
"""<number>
 
Returns login name of the developer the bug number <number> is assigned
to. Only positive integers are accepted as bug numbers.
"""
self.bugno = number
self.url = self.mantisutils._makeurl(self.server, self.bugno)
try:
self.soup = self.mantisutils._bugdata(self.url)
self.title = self.soup.html.head.title.contents
if self.title[0] == 'Mantis Issue Tracker for Scribus':
irc.reply("Sorry, it appears that this bug doesn't exist")
elif self.title == None:
irc.reply("Sorry, this bug is not accessible")
else:
self.assignee = self.soup.firstText(re.compile('Assigned\ To')).findNext('td').contents[0].strip()
irc.reply(self.assignee)
except Error, e:
irc.reply(str(e))
 
assignedto = wrap(assignedto, ['int'])
 
def checkbts(self):
""" Example: http://bugs.scribus.net/main_page.php"""
self.defaultsrv='bugs.scribus.net'
self.confsrv = conf.supybot.plugins.Mantis.server()
if not self.confsrv:
self.server = self.defaultsrv
else:
self.server = self.confsrv
self.btsurl = 'http://'+self.server+'/main_page.php'
#self.btsurl = 'http://'+self.defaultsrv+'/main_page.php'
try:
self.mainpage = urllib2.urlopen(self.btsurl)
self.log.debug('Checking the bts at %u', self.btsurl)
return True
except URLError:
print "Cannot open the BTS main page. Unloading plugin."
 
def bugUrl(self, irc, msg, m):
r"^#\d{4,9}"
"""Returns the URL for the bug number passed into the channel."""
self.msg = msg
self.channel = self.msg.args[0]
if irc.isChannel(self.channel):
if ircmsgs.isAction(self.msg):
self.text = ircmsgs.unAction(self.msg)
else:
self.text = self.msg.args[1]
self.buglist = re.findall(r"^#\d{4,9}", self.text)
for self.bugid in self.buglist:
self.bugno = self.bugid.rstrip()[1:]
self.url = self.mantisutils._makeurl(self.server,self.bugno)
try:
self.soup = self.mantisutils._bugdata(self.url)
self.title = self.soup.html.head.title.contents
if self.title[0] == 'Mantis Issue Tracker for Scribus':
irc.reply("Sorry, it appears that this bug doesn't exist")
elif self.title == None:
irc.reply("Sorry, this bug is not accessible")
else:
self.shorttitle = self.title[0].strip().split(" - Mantis Issue Tracker")[0]
irc.reply(self.shorttitle)
except:
irc.reply("Sorry, an unknown error has occured. I'll go cry for a while")
else:
self.log.debug("We are not in a channel")
 
Class = Mantis
 
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
/tools/mrscribe/Mantis/plugin.py
0,0 → 1,275
###
# Copyright (c) 2006, 2007, Oleksandr Moskalenko
# All rights reserved.
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
###
 
import supybot.conf as conf
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.ircmsgs as ircmsgs
import supybot.callbacks as callbacks
import urllib2
import re
from BeautifulSoup import BeautifulSoup
#from BeautifulSoup import Null
 
class Utils:
"""This class contains utils used by several commands."""
def __init__(self):
pass
def _makeurl(self, server, bugno):
self.bugnumber = str(bugno)
self.server = server
self.url = 'http://'+self.server+'/view.php?id='+self.bugnumber
return self.url
def _bugdata(self, url):
self.url = url
try:
self.srchtml = urllib2.urlopen(self.url).read()
except urllib2.URLError:
print "Cannot fetch the bug page."
self.soup = BeautifulSoup(self.srchtml)
return self.soup
 
class Mantis(callbacks.PluginRegexp):
"""This plugin is non-interactive. No help is needed."""
regexps = ['bugUrl'] #,'titleSnarfer']
def __init__(self, irc):
self.__parent = super(Mantis, self)
self.__parent.__init__(irc)
if self.checkbts() == True:
self.btsstatus = 'Mantis BTS is accessible'
else:
self.btsstatus = 'Cannot access the BTS front page'
self.mantisutils = Utils()
 
def bugtitle(self, irc, msg, args, number):
"""<number>
 
Returns the Title of the bug number <number>. Only positive integers
are accepted as bug numbers.
"""
self.bugno = number
self.url = self.mantisutils._makeurl(self.server, self.bugno)
try:
self.soup = self.mantisutils._bugdata(self.url)
self.title = self.soup.html.head.title.contents
if self.title[0] == 'Mantis Issue Tracker for Scribus':
irc.reply("Sorry, it appears that this bug doesn't exist")
elif self.title == None:
irc.reply("Sorry, this bug is not accessible")
else:
self.shorttitle = self.title[0].strip().split(" - Mantis Issue Tracker")[0]
irc.reply(self.shorttitle)
except Error, e:
irc.reply("Error in 'bugtitle' call.")
irc.reply(str(e))
 
bugtitle = wrap(bugtitle, ['int'])
 
def bugstatus(self, irc, msg, args, number):
"""<number>
 
Returns the status of the bug number <number>. Only positive integers
are accepted as bug numbers.
"""
self.bugno = number
self.url = self.mantisutils._makeurl(self.server, self.bugno)
try:
self.soup = self.mantisutils._bugdata(self.url)
self.title = self.soup.html.head.title.contents
if self.title[0] == 'Mantis Issue Tracker for Scribus':
irc.reply("Sorry, it appears that this bug doesn't exist")
elif self.title == None:
irc.reply("Sorry, this bug is not accessible")
else:
self.status = self.soup.firstText(re.compile('--\ Status')).findNext('td').findNext('td').contents[0].strip()
irc.reply(self.status)
except Error, e:
irc.reply("Error in 'bugstatus' call.")
irc.reply(str(e))
 
bugstatus = wrap(bugstatus, ['int'])
 
def bugreporter(self, irc, msg, args, number):
"""<number>
 
Returns the reporter of the bug number <number>. Only positive integers
are accepted as bug numbers.
"""
self.bugno = number
self.url = self.mantisutils._makeurl(self.server, self.bugno)
try:
self.soup = self.mantisutils._bugdata(self.url)
self.title = self.soup.html.head.title.contents
if self.title[0] == 'Mantis Issue Tracker for Scribus':
irc.reply("Sorry, it appears that this bug doesn't exist")
elif self.title == None:
irc.reply("Sorry, this bug is not accessible")
else:
self.reporter = self.soup.firstText(re.compile('Reporter')).findNext('td').findNext('td').contents[0].strip()
irc.reply(self.reporter)
except Error, e:
irc.reply("Error in 'bugreporter' call.")
irc.reply(str(e))
 
bugreporter = wrap(bugreporter, ['int'])
 
def bugassign(self, irc, msg, args, number):
"""<number>
 
Returns login name of the developer the bug number <number> is assigned
to. Only positive integers are accepted as bug numbers.
"""
self.bugno = number
self.url = self.mantisutils._makeurl(self.server, self.bugno)
try:
self.soup = self.mantisutils._bugdata(self.url)
self.title = self.soup.html.head.title.contents
if self.title[0] == 'Mantis Issue Tracker for Scribus':
irc.reply("Sorry, it appears that this bug doesn't exist")
elif self.title == None:
irc.reply("Sorry, this bug is not accessible")
else:
self.assignee = self.soup.firstText(re.compile('Assigned\ To')).findNext('td').contents[0].strip()
irc.reply(self.assignee)
except Error, e:
irc.reply("Error in 'bugassign' call.")
irc.reply(str(e))
 
bugassign = wrap(bugassign, ['int'])
 
def bugsummary(self, irc, msg, args, number):
"""<number>
 
Returns Summary of the bug number <number>. Only positive integers
are accepted as bug numbers.
"""
self.bugno = number
self.url = self.mantisutils._makeurl(self.server, self.bugno)
try:
self.soup = self.mantisutils._bugdata(self.url)
self.title = self.soup.html.head.title.contents
if self.title[0] == 'Mantis Issue Tracker for Scribus':
irc.reply("Sorry, it appears that this bug doesn't exist")
elif self.title == None:
irc.reply("Sorry, this bug is not accessible")
else:
self.s = self.soup.find(text=re.compile("Summary"))
self.indata = self.s.next.next.td.nextSibling.nextSibling
self.outdata = self.indata.contents[0].encode('utf8').split(":")[1].strip()
irc.reply(self.outdata)
except Error, e:
irc.reply("Error in 'bugsummary' call.")
irc.reply(str(e))
 
bugsummary = wrap(bugsummary, ['int'])
 
def bugdesc(self, irc, msg, args, number):
"""<number>
 
Returns Description of the bug number <number>. Only positive integers
are accepted as bug numbers.
"""
self.bugno = number
self.url = self.mantisutils._makeurl(self.server, self.bugno)
try:
self.soup = self.mantisutils._bugdata(self.url)
self.title = self.soup.html.head.title.contents
if self.title[0] == 'Mantis Issue Tracker for Scribus':
irc.reply("Sorry, it appears that this bug doesn't exist")
elif self.title == None:
irc.reply("Sorry, this bug is not accessible")
else:
self.s = self.soup.find(text=re.compile("Description"))
self.indata = self.s.next.next.td.nextSibling.nextSibling
self.outdata = self.indata.contents[0].encode('utf8').strip()
irc.reply(self.outdata)
except Error, e:
irc.reply(str(e))
 
bugdesc = wrap(bugdesc, ['int'])
 
 
def bugurl(self, irc, msg, args, number):
"""Returns the URL for the bug number passed into the channel."""
self.bugno = number
self.url = self.mantisutils._makeurl(self.server, self.bugno)
try:
self.soup = self.mantisutils._bugdata(self.url)
self.title = self.soup.html.head.title.contents
if self.title[0] == 'Mantis Issue Tracker for Scribus':
irc.reply("Sorry, it appears that this bug doesn't exist")
elif self.title == None:
irc.reply("Sorry, this bug is not accessible")
else:
irc.reply(self.url)
except Error, e:
irc.reply(str(e))
 
bugurl = wrap(bugurl, ['int'])
 
def checkbts(self):
""" Example: http://bugs.scribus.net/main_page.php"""
self.defaultsrv='bugs.scribus.net'
self.confsrv = conf.supybot.plugins.Mantis.server()
if not self.confsrv:
self.server = self.defaultsrv
else:
self.server = self.confsrv
self.btsurl = 'http://'+self.server+'/main_page.php'
#self.btsurl = 'http://'+self.defaultsrv+'/main_page.php'
try:
self.mainpage = urllib2.urlopen(self.btsurl)
self.log.debug('Checking the bts at %u', self.btsurl)
return True
except URLError:
print "Cannot open the BTS main page. Unloading plugin."
 
def bugUrl(self, irc, msg, m):
r"^#\d{4,9}"
"""Returns the URL for the bug number passed into the channel."""
self.msg = msg
self.channel = self.msg.args[0]
if irc.isChannel(self.channel):
if ircmsgs.isAction(self.msg):
self.text = ircmsgs.unAction(self.msg)
else:
self.text = self.msg.args[1]
self.buglist = re.findall(r"^#\d{4,9}", self.text)
for self.bugid in self.buglist:
self.bugno = self.bugid.rstrip()[1:]
self.url = self.mantisutils._makeurl(self.server,self.bugno)
try:
self.soup = self.mantisutils._bugdata(self.url)
self.title = self.soup.html.head.title.contents
if self.title[0] == 'Mantis Issue Tracker for Scribus':
irc.reply("Sorry, it appears that this bug doesn't exist")
elif self.title == None:
irc.reply("Sorry, this bug is not accessible")
else:
self.shorttitle = self.title[0].strip().split(" - Mantis Issue Tracker")[0]
irc.reply(self.shorttitle)
except:
irc.reply("Sorry, an unknown error has occured. I'll go cry for a while")
else:
self.log.debug("We are not in a channel")
 
Class = Mantis
 
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
/tools/mrscribe/Mantis/__init__.py
0,0 → 1,50
###
# Supybot plugin for querying a Mantis BTS
# Copyright (c) 2006, 2007, Oleksandr Moskalenko
# All rights reserved.
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
###
 
"""
This pluging produces expanded urls for Mantis BTS bug numbers.
"""
 
import supybot
import supybot.world as world
 
# Use this for the version of this plugin. You may wish to put a CVS keyword
# in here if you're keeping the plugin in CVS or some similar system.
__version__ = "0.3"
__author__ = supybot.Author('Oleksandr Moskalenko', 'malex', 'malex@tagancha.org')
__contributors__ = {}
 
# This is a url where the most recent plugin package can be downloaded.
__url__ = 'http://linux.tagancha.org/software/supybot/Mantis'
 
import config
import plugin
reload(plugin) # In case we're being reloaded.
reload(config) # In case we're being reloaded.
# Add more reloads here if you add third-party modules and want them to be
# reloaded when this plugin is reloaded. Don't forget to import them as well!
 
if world.testing:
import test
 
Class = plugin.Class
configure = config.configure
 
 
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
/tools/mrscribe/Mantis/test.py
0,0 → 1,37
###
# Copyright (c) 2006, Oleksandr Moskalenko
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
 
###
 
from supybot.test import *
 
class MantisTestCase(PluginTestCase):
plugins = ('Mantis',)
 
 
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
/tools/mrscribe/Mantis/README.txt
0,0 → 1,3
This is a simple plugin that monitors for the Mantis BTS bug number-like
strings e.g. "#3185" and produces proper URL for those bugs. It is supposed to
be configured for the particular installation of Mantis.
/tools/mrscribe/Mantis/config.py
0,0 → 1,63
###
# Copyright (c) 2006, Oleksandr Moskalenko
# All rights reserved.
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
 
###
 
import supybot.conf as conf
import supybot.registry as registry
 
def configure(advanced):
# This will be called by supybot to configure this module. advanced is
# a bool that specifies whether the user identified himself as an advanced
# user or not. You should effect your configuration by manipulating the
# registry as appropriate.
from supybot.questions import expect, anything, something, yn
Mantis = conf.registerPlugin('Mantis', True)
output('The default Mantis BTS server is bugs.scribus.net.')
if yn('Would you like to specify a different BTS server?'):
server = something('What server?')
conf.supybot.plugins.Mantis.server.set(server)
 
 
Mantis = conf.registerPlugin('Mantis')
 
conf.registerGlobalValue(Mantis, 'server',
registry.String('bugs.scribus.net', """Determines the hostname for the
Mantis BTS server."""))
conf.registerGlobalValue(Mantis, 'titleSnarfer',
registry.Boolean(False, """Determines whether the bot will output the HTML
title of URLs it sees in the channel."""))
 
 
#http://bugs.scribus.net/view.php?id=3509
 
# This is where your configuration variables (if any) should go. For example:
# conf.registerGlobalValue(Mantis, 'someConfigVariableName',
# registry.Boolean(False, """Help for someConfigVariableName."""))
 
#conf.registerChannelValue(Mantis, 'unknown',
# Mantis('btsurl', """Determines what URL the bot will prepend to bug numbers."""))
 
#conf.registerGlobalValue(Mantis, 'server',
# registry.String('dict.org', """Determines what server the bot will
# retrieve definitions from."""))
#conf.registerChannelValue(Dict, 'default',
# registry.String('', """Determines the default dictionary the bot will
# ask for definitions in. If this value is '*' (without the quotes) the bot
# will use all dictionaries to define words."""))
 
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
/tools/mrscribe/Svnwatch/plugin.py
0,0 → 1,139
###
# Copyright (c) 2007, Oleksandr Moskalenko
# All rights reserved.
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
###
import svnxmlparser
 
import threading
import time
 
from supybot.commands import *
import supybot.conf as conf
import supybot.utils as utils
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.ircmsgs as ircmsgs
import supybot.registry as registry
import supybot.callbacks as callbacks
 
class Svnwatch(callbacks.Plugin):
"""Main class of the plugin."""
threaded = True
def __init__(self, irc):
self.__parent = super(Svnwatch, self)
self.__parent.__init__(irc)
try:
self.xmluri = self.registryValue('xmluri')
self.wait = self.registryValue('fetchPeriod')
self.postfrequency = self.registryValue('postFrequency')
self.channels = self.registryValue('channels')
self.lastrev = self.registryValue('lastRevision')
self.lastfetch = time.time() - self.wait - 1
self.log.debug("%s; %s, %s, %s, %s" % (self.xmluri, self.wait, self.postfrequency, self.channels, self.lastrev))
except registry.NonExistentRegistryEntry:
self.log.debug('%s is not a registered URI.', self.xmluri)
irc.error('Can\'t get configuration variables.', Raise=True)
self.getCommit(irc)
 
def canGetNewCommit(self, irc):
"""Checks if more then fetchPeriod time lapsed since our last fetch.
Returns True is that's the case and False if we need to wait longer."""
self.now = time.time()
self.log.debug("Current time: %s; Last fetch: %s; Difference: %d sec" % (self.now, self.lastfetch, int(self.now - self.lastfetch)))
if self.now - self.lastfetch > self.wait:
self.log.debug("Enough time passed, fetching new commit...")
return True
else:
self.log.debug("Not enough time passed, standing by...")
return False
 
def getHead(self, xmluri):
"""Fetches the head revision number for internal processing. Returns 'HEAD'
in case of error or misconfiguration."""
self.xmluri = xmluri
self.rev = "HEAD"
self.rawxml = svnxmlparser.fetchXml(self.xmluri, self.rev)
self.commitdata = svnxmlparser.getCommit(self.rawxml)
if self.commitdata == None:
self.log.info("Cannot fetch revision %s" % (self.rev))
# raise "Cannot fetch head revision id"
return None
else:
return self.commitdata.keys()[0]
 
def getCommit(self, irc):
self.channels = self.registryValue('channels')
self.log.debug("Reporting channels: %s", self.channels)
self.lastrev = self.registryValue('lastRevision')
# if self.canGetNewCommit(irc):
try:
self.log.debug('Fetching HEAD revision number from %s', self.xmluri)
self.headid = self.getHead(self.xmluri)
self.log.debug('Current HEAD revision number: %s', self.headid)
if self.headid == None:
self.log.debug('Error occured while fetching HEAD revision from %s', self.xmluri)
self.now = time.time()
self.lastfetch = self.now
self.log.debug("Failed to fetch 'HEAD' at %s" % (self.now))
elif int(self.headid) == self.lastrev:
self.log.debug("Fetched 'HEAD' - no new commmits at %s", self.xmluri)
self.now = time.time()
self.lastfetch = self.now
self.log.debug("Fetched new 'HEAD' revision at %s" % (self.now))
else:
self.oldestnewrev = self.lastrev + 1
self.revid = "%s:HEAD" % self.oldestnewrev
self.revuri = self.xmluri + "?" + self.revid
self.log.debug("Fetching new commit(s) for %s" % self.revuri)
self.rawxml = svnxmlparser.fetchXml(self.xmluri, self.revid)
self.log.debug("Fetched xml %s" % self.rawxml)
self.resultcommitdata = svnxmlparser.getCommit(self.rawxml)
self.log.debug("Fetched data %s" % self.resultcommitdata)
self.shortdata = svnxmlparser.shortoutput(self.resultcommitdata)
self.log.debug("Short data: %s" % self.shortdata)
if len(self.shortdata) == 1:
self.revisiondata = " ".join(self.shortdata)
self.log.debug("Formatted data: %s" % self.revisiondata)
self.lastrev = int(self.revisiondata.split(" ")[1])
self.log.debug("Revision ID: %s" % self.lastrev)
for self.channel in self.channels:
irc.replies(self.revisiondata, joiner='', to=self.channel, prefixNick=False, private=True)
else:
for self.revisiondata in self.shortdata:
self.log.debug("Revision data: %s" % self.revisiondata)
self.lastrev = int(self.revisiondata.split(" ")[1])
self.log.debug("Last revision ID: %s" % self.lastrev)
for self.channel in self.channels:
irc.replies(self.revisiondata, joiner='', to=self.channel, prefixNick=False, private=True)
self.now = time.time()
self.lastfetch = self.now
conf.supybot.plugins.Svnwatch.lastRevision.setValue(self.lastrev)
self.log.debug("Fetched new commit(s) up to revision %s at %s"
% (self.lastrev, self.now))
except:
raise
 
def __call__(self, irc, msg):
self.__parent.__call__(irc, msg)
irc = callbacks.SimpleProxy(irc, msg)
if self.canGetNewCommit(irc):
self.log.debug("Invoking __call__")
self.getCommit(irc)
 
Class = Svnwatch
 
 
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
/tools/mrscribe/Svnwatch/__init__.py
0,0 → 1,56
###
# Supybot plugin for querying SVN commits and posting commit log
# Copyright (c) 2007, Oleksandr Moskalenko
# All rights reserved.
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
###
 
"""
This plugin posts SVN commit summary to a channell and allows queries for
specific revisions and partial commit data """
 
import supybot
import supybot.world as world
 
# Use this for the version of this plugin. You may wish to put a CVS keyword
# in here if you're keeping the plugin in CVS or some similar system.
__version__ = "0.1"
 
# Replace this with an appropriate author or supybot.Author instance.
__author__ = supybot.Author('Oleksandr Moskalenko', 'malex', 'malex@tagancha.org')
 
# This is a dictionary mapping supybot.Author instances to lists of
# contributions.
__contributors__ = {}
 
# This is a url where the most recent plugin package can be downloaded.
__url__ = 'http://linux.tagancha.org/software/supybot/Svnwatch'
 
import config
import plugin
import svnxmlparser
reload(plugin) # In case we're being reloaded.
reload(svnxmlparser) # In case we're being reloaded
# Add more reloads here if you add third-party modules and want them to be
# reloaded when this plugin is reloaded. Don't forget to import them as well!
 
if world.testing:
import test
 
Class = plugin.Class
configure = config.configure
 
 
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
/tools/mrscribe/Svnwatch/test.py
0,0 → 1,14
###
# Copyright (c) 2007, Oleksandr Moskalenko
# All rights reserved.
#
#
###
 
from supybot.test import *
 
class SvnwatchTestCase(PluginTestCase):
plugins = ('Svnwatch',)
 
 
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
/tools/mrscribe/Svnwatch/README.txt
0,0 → 1,4
This plugin will query a svn web front-end and fetch svn XML data related to
the commit information. It will immediately post the newest commit data to the
configured IRC channel(s). It will also create a local database of commits and
return full or partial commit data on request to registered users.
/tools/mrscribe/Svnwatch/svnxmlparser.py
0,0 → 1,209
#!/usr/bin/python
# Copyright (c) 2007, Oleksandr Moskalenko, Craig Ringer
# All rights reserved.
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
###
"""Subversion XML parser
"""
__version__ = "0.1"
__license__ = "GPLv2"
__copyright__ = "Copyright 2007, Oleksandr Moskalenko"
__author__ = "Oleksandr Moskalenko <malex@tagancha.org>"
__contributors__ = ["Craig Ringer <craig@postnewspapers.com.au>"]
_debug = 1
 
# HTTP "User-Agent" header to send to servers when downloading Svn XML.
USER_AGENT = "SubversionXMLParser/%s +http://linux.tagancha.org/svnxmlparser" % __version__
# HTTP "Accept" header to send to servers when downloading Svn XML. If you don't
# want to send an Accept header, set this to None.
ACCEPT_HEADER = "text/xml"
 
# Default URI:
URI = "https://www.postnewspapers.com.au/cgi-bin/xmllog"
 
def fetchXml(uri, rev):
"""Fetches a document from the given URI (with gzip encoding). Returns the
document as a string, or an empty string in case of a transmission problem."""
import urllib2, gzip, StringIO
try:
revuri = "%s?%s" % (uri,rev)
request = urllib2.Request(revuri)
request.add_header('Accept-encoding', 'gzip')
opener = urllib2.build_opener()
handle = opener.open(request)
compressedData = handle.read()
# print 'Fetched ', len(compressedData), ' bytes.'
compressedStream = StringIO.StringIO(compressedData)
gzipper = gzip.GzipFile(fileobj=compressedStream,mode='r')
xml = gzipper.read()
if len(xml) <= 28:
print "No commit for this revision\n"
return None
return xml
except:
return None
 
def longest_common_prefix_for_paths(paths):
'''
Find the longest common prefix of `paths' and return it.
`paths' must be a list of strings, using / as a path component
separator.
'''
if len(paths) == 1:
# use just this path
return paths[0]
else:
# find common prefix
p = [ x.split('/') for x in paths ]
maxi = reduce( min, map( len, p ) )
for i in range(0, maxi):
if not reduce( lambda a,b: a and (b[i] == paths[0][i]),\
paths[1:], True):
break
return '/'.join(p[0][0:i])
 
def getText(nodelist):
rc = ""
for node in nodelist:
if node.nodeType == node.TEXT_NODE:
rc = rc + node.data
return rc
 
def getCommit (xml):
"""Parses Svn XML and returns a dictionary containing commit data."""
if xml == None:
return xml
from xml.dom import minidom, Node
xmltree = minidom.parseString(xml)
logentries = xmltree.getElementsByTagName('logentry')
logdata = {}
for logentry in logentries:
if logentry.hasAttributes():
revision = logentry.getAttribute('revision')
logdata[revision]= {}
logdata[revision]['author'] = logentry.getElementsByTagName('author')[0].childNodes[0].data
logdata[revision]['date'] = logentry.getElementsByTagName('date')[0].childNodes[0].data
try:
logdata[revision]['msg'] = logentry.getElementsByTagName('msg')[0].childNodes[0].data
except IndexError:
logdata[revision]['msg'] = ""
paths = []
for path in logentry.getElementsByTagName("paths")[0].childNodes:
if path.nodeType == Node.ELEMENT_NODE:
paths.append(path.childNodes[0].data)
logdata[revision]['paths'] = paths
common_path = longest_common_prefix_for_paths(paths)
logdata[revision]['common_path'] = common_path
short_paths = []
for path in paths:
try:
short_paths.append(path.split(common_path)[1][1:])
except IndexError:
pass
logdata[revision]['short_paths'] = short_paths
return logdata
 
def printoutput(commitdata):
datakeys = commitdata.keys()
datakeys.sort()
for i in datakeys:
logentry = commitdata[i]
logentry.keys().sort()
print "Revision: " + str(i)
for j in logentry.keys():
if type(logentry[j]) != list:
print "%s: \t %s" % (j,logentry[j].strip())
else:
print j+":"
for k in logentry[j]:
print "\t "+k
 
def getbranch(logentry):
import sys
fullpath = logentry['common_path']
pathelements = fullpath.split("/")
if pathelements[1] == 'trunk':
return 'trunk'
else:
return "%s" % pathelements[2]
 
def shortoutput(commitdata):
outlist = []
datakeys = commitdata.keys()
datakeys.sort()
for i in datakeys:
logentry = commitdata[i]
logentry.keys().sort()
from datetime import datetime
from time import strptime
indatetime = logentry["date"].split('.')[0]
rawdatetime = datetime(*strptime(indatetime, "%Y-%m-%dT%H:%M:%S")[0:6])
outdate,outtime = rawdatetime.strftime("%Y-%m-%d %H:%M:%S").split(" ")
branch = getbranch(logentry)
outstr = "revision %s by %s in %s on %s at %s - %s" % (i, logentry["author"], branch, outdate, outtime, logentry["msg"])
finalstr = outstr.replace("\n", " ")
outlist.append(finalstr)
return outlist
# for j in logentry.keys():
# #FIXME
# if type(logentry[j]) != list:
# print "%s: \t %s" % (j,logentry[j].strip())
# else:
# print j+":"
# for k in logentry[j]:
# print "\t "+k
 
def processUri(xmluri,rev):
print xmluri
rawxml = fetchXml(xmluri,rev)
commitdata = getCommit(rawxml)
if commitdata == None:
return None
else:
outdata = shortoutput(commitdata)
return outdata
 
def getHead(xmluri):
"""Fetches the head revision number for internal processing. Returns 'HEAD'
in case of error or misconfiguration."""
rev = "HEAD"
rawxml = fetchXml(xmluri,rev)
commitdata = getCommit(rawxml)
if commitdata == None:
return None
else:
return commitdata.keys()[0]
 
def Main ():
import sys
"""Called when the program is used interactively."""
if len(sys.argv)>1:
args = sys.argv[1:]
# print args
else:
args = ['HEAD']
for i in args:
print "Fetching commit information for '%s':" % i
xml = fetchXml(URI,i)
commitdata = getCommit(xml)
if commitdata == None:
sys.exit()
out = shortoutput(commitdata)
for i in out:
print i
sys.exit()
 
if __name__ == '__main__':
Main()
/tools/mrscribe/Svnwatch/config.py
0,0 → 1,59
###
# Copyright (c) 2007, Oleksandr Moskalenko
# All rights reserved.
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
###
 
import supybot.conf as conf
import supybot.registry as registry
import supybot.callbacks as callbacks
 
def configure(advanced):
# This will be called by supybot to configure this module. advanced is
# a bool that specifies whether the user identified himself as an advanced
# user or not. You should effect your configuration by manipulating the
# registry as appropriate.
from supybot.questions import expect, anything, something, yn
conf.registerPlugin('Svnwatch', True)
output('The default SVN xml URI is https://www.postnewspapers.com.au/cgi-bin/xmllog')
if yn('Would you like to specify a SVN xml URI?'):
xmluri = something('SVN XML URI?')
conf.supybot.plugins.Svnwatch.set(xmluri)
 
#class PostChannels(registry.SpaceSeparated):
# List = callbacks.CanonicalNameSet
 
Svnwatch = conf.registerPlugin('Svnwatch')
# This is where your configuration variables (if any) should go. For example:
# conf.registerGlobalValue(Svnwatch, 'someConfigVariableName',
# registry.Boolean(False, """Help for someConfigVariableName."""))
conf.registerGlobalValue(Svnwatch, 'xmluri',
registry.String('https://www.postnewspapers.com.au/cgi-bin/xmllog',
"""The uri for the exported SVN XML source."""))
conf.registerGlobalValue(Svnwatch, 'fetchPeriod',
registry.PositiveInteger(60, """Indicates how many seconds the
bot will wait between retrieving new svn commit
information."""))
conf.registerGlobalValue(Svnwatch, 'lastRevision',
registry.PositiveInteger(1000, """Indicates the last revision published in
the channels."""))
conf.registerGlobalValue(Svnwatch, 'postFrequency', registry.PositiveInteger(5,
"""Indicates how many seconds the bot will wait between posting svn commit
information messages."""))
conf.registerGlobalValue(Svnwatch,
'channels', conf.SpaceSeparatedSetOfChannels([], """Determines
which channels the bot will post SVN commit information to."""))
 
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: