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