It's a common use case to have your tracker system somehow integrated within your Source Control Manager. A simple way I found to do this is to have keywords in the item messages that expand to hyperlinks.
Hence, if you type in the item's note textbox something like 'This problem is solved in Version:35' the recipe I present here lets roundup expands such message with an hyperlink in 'Version:35' that points to a custom Versioning system.
This solution is very customizable and you may find many others useful cases.
Procedures
- In issue.item.html replace the line where it says:
"structure msg.content.hyperlinked"
by:"structure python:utils.localReplace(msg.content.hyperlinked())"
- In your 'extensions' directory, create a file 'local_replace.py' with::
- Add the custom_tags.py to a directory where Roundup can find it (I'd use python's
site-packages see footnote, or you can create a subdirectory named lib in your tracker home directory and put the file there.) customizing the 'substitute' attribute for your own needs. All the specific work you need to do is in this variable. Read the CustomTags class doc for details.
Ask any question at the roundup users mailing list or by email to hfoffani@gmail.com.
Regards,
-Hernan.
custom_tags.py:
1 import re
2 class CustomTags:
3 """Replace custom tags
4
5 Use this class if you want to do parametrized string substitution
6 in a given text. It's useful to expand some keywords with a full
7 html tag. Customize this class by changing the 'substitute'
8 attribute. This attribute is a list of tuples. Each tuple
9 represents a substitution task. The first item of the tuple is
10 the lookup regular expression, the second item is the replace
11 string template. Just be careful to match the names in the
12 regular expression, eg. '(?P<a_name>...)', with the corresponding
13 ones in the template, eg. '... %(a_name)s ...'
14
15 A simple case:
16 >>> ct = CustomTags()
17 >>> ct.substitute = [('mom','papa')]
18 >>> ct.replace("hey! look mom! no hands!")
19 'hey! look papa! no hands!'
20
21 A more complex one:
22 >>> ct.substitute = [ (
23 ... r'(?P<kw>rev):(?P<int>\d*)',
24 ... r'<a href="http://%(kw)s?%(int)s">%(kw)s:%(int)s</a>'
25 ... ) ]
26 >>> ct.replace("this is rev:33. but not rev:44")
27 'this is <a href="http://rev?33">rev:33</a>. but not <a href="http://rev?44">rev:44</a>'
28 >>> ct.replace("this remains untouched")
29 'this remains untouched'
30
31 More than one substitution task:
32 >>> ct = CustomTags()
33 >>> ct.substitute = [
34 ... ( r'mom', r'papa' ),
35 ... ( # a more complex one
36 ... r'(?P<kw>rev):(?P<int>\d*)',
37 ... r'<a href="http://%(kw)s?%(int)s">%(kw)s:%(int)s</a>')
38 ... ]
39 >>> ct.replace("mom, look for rev:1")
40 'papa, look for <a href="http://rev?1">rev:1</a>'
41
42 Do nothing:
43 >>> ct = CustomTags()
44 >>> ct.substitute = [ ]
45 >>> ct.replace("this should be untouched.")
46 'this should be untouched.'
47
48 For better performance you may precompile the regexp adding a
49 call to 're.compile(..)' to the first member of each tuple in
50 the 'substitute' list.
51 """
52
53 substitute = [
54 # ( r'mom', r'papa' ),
55 # ( r'(?P<kw>rev):(?P<int>\d*)',
56 # r'<a href="http://%(kw)s?%(int)s">%(kw)s:%(int)s</a>' )
57 ]
58
59 def replace(self, text):
60 """Returns a copy of text with its contents replaced."""
61
62 ntext = text
63 for regex, template in self.substitute:
64 for match in re.finditer(regex, text):
65 ntag = template % match.groupdict()
66 ntext = ntext.replace(match.group(0), ntag, 1)
67 return ntext
68
69 if __name__ == "__main__":
70 import doctest
71 doctest.testmod()
How to use site-packages for custom scripts.
- Make a subdirectory under 'PYTHONPATH/Lib/site-packages' where you can place your convenience files, say 'mycompany'. Python won't touch those on future upgrades.
Create an empty file called 'init.py' (that's two underscores before and after.)
- Place 'custom_tags.py' there. In this case 'local_replace.py' must import this file
as: 'from mycompany.custom_tags import CustomTags'
Back to roundup wiki's FrontPage