Introduction
For those people that would like to have a spell checker in their tracker, well here is one using aspell.
The spell checker is only executed for the web interface and not for the mail gateway. I guess it's safe to assume that e-mail clients can do the spell check for us, so there's no need to check the spelling on issues received from the mail gateway.
The spell checker will check all words with exception of upper case words.
Those will be skipped. In some cases we want to skip the spell check for
all words. Therefor a check box can be checked. If checked, the spell check
is skipped and all text is accepted as it is.
If a spell check is performed and spelling mistakes are found, the form will
be returned with an enumeration of all misspelled words. If available, then
suggestions will be given for those words to.
To make it easier to lookup those misspelled words, they will be marked with
stars like *word*. Just go trough the text, correct the marked words
(or write them in uppercase to skip them) and submit the issue again. And
if you don't want the words to be skipped in uppercase, then just correct
the words which need to be corrected, check the No spell checking check box
and re-submit the form.
Requirements
To use this feature, the next software if required on your server:
- aspell (http://aspell.sourceforge.net)
- aspell-python (http://www.republika.pl/wmula/proj/aspell-python/index.html)
Implementation
The spell checker is implemented in two action handlers and not in auditors.
This ensures us that the spell checker will only execute for the
web interface and not for the mail gateway.
The new action handlers only do the spell checker part. The rest of the handling
is still done by the original roundup action handlers. This will keep our new
action handlers plain and simple.
Both action handlers are almost the same. The difference is that one is for
new issues while the other one is for updating an issue.
To use the action handlers, only minor changes are required.
Regretfully the classic issue.item.html template uses a roundup method to
put the submit button on the form together with the required hidden input
fields that will tell roundup what to do, so we need to make some small
changes to call the spell checker action handler in issue.item.html.
Even so do we need to tell the action handler which form fields to check.
This is done by adding an hidden input tag to the template. This hidden
tag looks like:
<input type="hidden" name="@aspell" value="<name>:<field>,...">
For the default classic template it will be:
<input type="hidden" name="@aspell" value="title:title,@note:message">
The above line will perform spell checking on HTML form fields title and @note.
If errors are found in these fields, the error message will refer to the fields
title and message (like they are named on the page).
Best regards,
Marlon van den Berg
Source Code
These are the two new action handlers. Copy the content to a file and save it in folder '<tracker_home>/extensions':
class AspellClass:
def check(self):
"""\
Check if a form input field has some spelling mistakes.
If it has, it returns a tuple with a dictionary on the
first tuple index and the marked up text on the second
tuple index.
The keys of the dictionary contain all the spelling
mistakes. The items contain a tuple with suggestions
for the word.
The marked up text is the original input form field
with '*' around the spelling mistakes.
"""
def __inner(text):
"""\
Does all the work.
"""
import aspell
import re
# select english language
sp = aspell.Speller('lang', 'en')
# split the text into list of words
# (excluding punctuation marks)
word_list = re.split('\W+', text)
new_text = text
errors = {}
for word in word_list:
# skip empty words (not words at all) and
# skip upercase words
if word and not word.isupper():
word = word.lower()
# if it isn't checked before
if not errors.has_key(word):
# perform spellcheck on word
if not sp.check(word):
# word incorrect
suggestions = sp.suggest(word)
if suggestions:
# put spelling mistake in error list with suggestions
errors[word] = suggestions
# put some markings around the word
res = re.compile('\\b(%s)\\b'%word, re.IGNORECASE)
new_text = res.sub('*\\1*', new_text)
return (errors, new_text)
if self.form.has_key('no_spell_check') \
and self.form['no_spell_check'].value == '1':
# no spell check requested
return None
errors = []
if self.form.has_key('@aspell'):
_aspell = '@aspell'
elif self.form.has_key(':aspell'):
_aspell = ':aspell'
else:
_aspell = None
if _aspell:
for field in self.form[_aspell].value.split(','):
form_name, nice_name = field.split(':')
if self.form.has_key(form_name):
new_errors, text = __inner(self.form[form_name].value)
if new_errors:
# update form text (will place the markings around
# the spelling mistakes)
self.form[form_name].value = text
if errors:
# more than one form field has a spelling mistake
errors.append('<br>')
# tell the user which form field has the spelling mistake
errors.append("<i>%s</i> contains spelling error(s):"%nice_name)
# add each spelling mistake to the error message
for word, suggest in new_errors.items():
errors.append('''<br>\
<b style="color:yellow">%s</b> - <select><option>%s</option></select>'''% \
(word, '</option><option>'.join(suggest)))
self.form.value.remove(self.form[_aspell])
return errors
class Aspell_NewItemAction(AspellClass, NewItemAction):
def handle(self):
# perform spell check
errors = self.check()
if errors:
# we have some spelling mistakes
self.client.error_message.extend(errors)
return
else:
# let RoundUp do the rest
NewItemAction.handle(self)
class Aspell_EditItemAction(AspellClass, actions.EditItemAction):
def handle(self):
# perform spell check
errors = self.check()
if errors:
# we have some spelling mistakes
self.client.error_message.extend(errors)
return
else:
# let RoundUp do the rest
actions.EditItemAction.handle(self)
def init(instance):
instance.registerAction('aspell_new_item', Aspell_NewItemAction)
instance.registerAction('aspell_edit_item', Aspell_EditItemAction)
Here are the changes for the issue.item.html template.
Lookup this part:
<td colspan=3>
<span tal:replace="structure context/submit">submit button</span>
<a tal:condition="context/id" tal:attributes="href context/copy_url"
i18n:translate="">Make a copy</a>
</td>
and replace it with:
<td colspan=3>
<tal:block tal:condition="not:context/id">
<input type="hidden" name="@action" value="aspell_new">
<input type="submit" name="submit" value="Submit New Entry">
</tal:block>
<tal:block tal:condition="context/id">
<input type="hidden" name="@lastactivity" tal:attributes="value context/activity">
<input type="hidden" name="@action" value="aspell_edit">
<input type="submit" name="submit" value="Submit Changes">
<a tal:attributes="href context/copy_url" i18n:translate="">Make a copy</a>
</tal:block>
<input type="hidden" name="@aspell" value="title:title,@note:message">
<input type="checkbox" name="no_spell_check" value="1">No spell checking
</td>