Log note : Functional auditor provided
added:

Here is just such an auditor, contributed by "Kyle Lanclos":http://www.ucolick.org/~lanclos/. This auditor has been tested with Roundup 1.3.2::



 # Prevent specific roundup usernames from appearing in an issue's nosy list.

 # The value designated as 'email' in the config.ini is already omitted
 # from the nosy list, but any aliases for the same will be treated as
 # regular roundup users.

 #
 # #
 # Definitions.
 # #
 #

 # Define a python list of user names (note: NOT e-mail addresses, and
 # NOT user ids) to omit from the nosy list.

 #omit_user_list = ['trackeralias','anotheralias']

 omit_user_list = []

 # Set DEBUG to True to get debug output to a file.

 DEBUG = False

 if DEBUG:
        debug_log_filename = '/tmp/nosyauditor.log'
        debug_log_filemode = 0666

 #
 # #
 # End of definitions. No changes should be required below this point.
 # #
 #

 #
 # #
 # Required modules.
 # #
 #

 # no imports required.

 #
 # #
 # Function definitions.
 # #
 #

 def intStringSort (string_int1, string_int2):
        ''' Take two strings representing integers, and perform a
            standard integer comparison.
        '''

        int1 = int(string_int1)
        int2 = int(string_int2)

        if int1 > int2:
                return 1

        if int1 < int2:
                return -1

        return 0


 def validateNosyUsers (db, cl, nodeid, new_values):
        ''' Remove any unwanted usernames (as designated above in
            omit_user_list) from the old + new nosy lists, if any.
        '''

        if DEBUG:
                import os
                debug_log_file = open (debug_log_filename, 'a')
                os.chmod (debug_log_filename, debug_log_filemode)


        # nodeid will be None if this is a new issue

        if nodeid == None:
                old_nosy_list = []
        else:
                # Populate the old_nosy_list from the existing
                # issue information. Explicitly make a copy of
                # the old list, to avoid unnecessarily modifying
                # the old values.

                old_nosy_list = list (cl.get (nodeid, 'nosy'))


        # Acquire the new nosy list, if any. Explicitly make a copy
        # of the new list, to avoid unnecessarily modifying the new
        # values.

        if new_values.has_key('nosy'):
                # The new nosy list is all we care about if it is
                # being actively modified.

                new_nosy_list = list (new_values.get ('nosy', []))
                full_nosy_list = new_nosy_list

        else:
                # If the nosy element is not being modified,
                # initialize the full list from the old nosy list.

                new_nosy_list = []
                full_nosy_list = old_nosy_list


        # Don't assume that these lists are sorted; if they are
        # not properly sorted, the comparison at the end of the
        # function may fail.

        full_nosy_list.sort (intStringSort)

        if DEBUG:
                debug_log_file.write ("Old nosy list: %s\n" % (old_nosy_list))
                debug_log_file.write ("New nosy list: %s\n" % (new_nosy_list))
                debug_log_file.write ("Full nosy list: %s\n" % (full_nosy_list))


        # Determine a username -> userid mapping for entries in omit_user_list.

        omit_userid_list = []

        for omit_username in omit_user_list:

                userid_list = db.user.stringFind (username=omit_username)

                omit_userid_list = omit_userid_list + userid_list

                if DEBUG:
                        debug_log_file.write ("Translated '%s' to user id '%s'.\n" % (omit_username, userid_list))

        omit_userid_list.sort (intStringSort)

        if DEBUG:
                debug_log_file.write ("User IDs to omit: %s\n" % (omit_userid_list))



        # Check our complete full_nosy_list for usernames in the
        # omit_user_list list.

        checked_nosy_list = []

        for nosy_userid in full_nosy_list:
                if nosy_userid in omit_userid_list:
                        if DEBUG:
                                debug_log_file.write ("Omitting user id '%s'.\n" % (nosy_userid))

                        pass
                else:
                        checked_nosy_list.append (nosy_userid)

        if DEBUG:
                debug_log_file.write ("Validated nosy list: %s\n" % (checked_nosy_list))


        # If the validated list is not the same as what's being processed
        # in this request, update the list.

        if full_nosy_list != checked_nosy_list:
                new_values['nosy'] = checked_nosy_list


        if DEBUG:
                debug_log_file.close()

        # End of validateNosyUsers

 #
 # #
 # Required hooks for roundup auditors. The third
 # argument is the priority; note that we are explicitly
 # requesting non-standard priorities.
 #
 # The first pass at priority 90 catches any bad usernames
 # before the nosy detectors run at default priority (100);
 # this prevents the detector from sending e-mails users already
 # on an issue's nosy list, but were recently added to the
 # omit list above.
 #
 # The second pass at priority 110 cleans up any bad usernames
 # that may have been added to the nosy list by the nosy detector
 # itself; for example, a receipient on the To: line of an
 # initial message for an issue (if your roundup is configured
 # to add such users to the nosy list).
 # #
 #

 def init(db):
        db.issue.audit('create', validateNosyUsers, 90)
        db.issue.audit('set',    validateNosyUsers, 90)
        db.issue.audit('create', validateNosyUsers, 110)
        db.issue.audit('set',    validateNosyUsers, 110)