#!/usr/bin/env python
# -*- coding: iso8859-1 -*-
#
# Author: Jonas Borgström <jonas@edgewall.com>
#
# This script will enforce the following policy:
#
#  "A checkin must reference an open ticket."
#
# This script should be invoked from the subversion pre-commit hook like this:
#
# LINUX:
#
#  REPOS="$1"
#  TXN="$2"
#  TRAC_ENV="/somewhere/trac/project/"
#  LOG=`/usr/bin/svnlook log -t "$TXN" "$REPOS"`
#  /usr/bin/python /some/path/trac-pre-commit-hook "$TRAC_ENV" "$LOG" || exit 1
#
# WINDOWS: sample of pre-commit.bat (must use temporary files for the log message)
#
##SET REPOS=%1
##SET TXN=%2
##
##::-----------------------------
##::Call the TRAC pre-commit hook
##::
##SET TRAC_ENV=C:\somewhere\trac\project
##SET LOG_FILE=%TEMP%.\svnfileT-%TXN%
##
##svnlook log -t %TXN% %REPOS%>%LOG_FILE%
##
##python [trac-path]\contrib\trac-pre-commit-hook "%TRAC_ENV%" "file:%LOG_FILE%"
##IF ERRORLEVEL 1 SET TRAC_CANCEL=YES
##DEL %LOG_FILE%
##IF DEFINED TRAC_CANCEL GOTO :ERROR
##::
##::-----------------------------
##
##:SUCCESS
##EXIT 0
##
##:ERROR
##EXIT 1 
##

import os
import re
import sys
import sqlite

def main():
    if len(sys.argv) != 3:
        print >> sys.stderr, 'Usage: %s <trac_project> <log_message>' % sys.argv[0]
        sys.exit(1)

    env_path = sys.argv[1]
    log = sys.argv[2]
    log = _readFromFile( log )

    tickets = []
    for tmp in re.findall('(?:closes|fixes|addresses|references|refs|re)'
                          '.?(#[0-9]+(?:(?:[, &]+| *and *)#[0-9]+)*)', log):
        tickets += re.findall('#([0-9]+)', tmp)
    
    # At least one ticket has to be mentioned in the log message
    if tickets == []:
        print >> sys.stderr, 'At least one open ticket must be mentioned ' \
              'in the log message.'
        sys.exit(1)

    cnx = sqlite.connect(os.path.join(env_path, 'db', 'trac.db'), timeout=10000)
    cursor = cnx.cursor()
    cursor.execute("SELECT COUNT(id) FROM ticket WHERE "
                   "status <> 'closed' AND id IN (%s)" % ','.join(tickets))
    row = cursor.fetchone()
    # At least one of the tickets mentioned in the log messages has to
    # be open
    if not row or row[0] < 1:
        print >> sys.stderr, 'At least one open ticket must be mentioned ' \
              'in the log message.'
        sys.exit(1)
    else:
        sys.exit(0)

def _readFromFile(message):
    """ Ivan Melnychuk: SOLUTION FOR WINDOWS SYSTEMS:
    Windows does not support returning values from script.
    Therefore temporary files may be used to keep some information like commit message
    If the 'message' starts with 'file:', and the file with indicated name exists, then read the text message from the file rather then using the text directly
    Even though actual message also may start with such prefix, it is very unlikely that the file exists with such a name by accident
    """
    if message[:5] == 'file:' and os.path.exists( message[5:] ):
        f = open( message[5:], 'r' )
        message = f.read()
        f.close()
        return message
    return message;

if __name__ == '__main__':
    main()




