#!/usr/bin/python
# post-commit.py
# symlinked in /path/to/repository/hooks/post-commit

import pysvn
import sys
import os
from datetime import datetime

# Begin Django bootstrap code

# You'll need to change this to match your own specific host /
# development path.

host_dir = '/var/www/hosts/example.com' 
site_dir = '%s/site' % host_dir
site_root = '%s/root' % site_dir

# I keep various versions of Django, and supporting Django apps,
# outside of the normal Python path. If you install via setup.py
# you might not need to loop this.

for p in ('/usr/local/codebase/framework', 
          '/usr/local/codebase/apps',
          site_root,
          '%s/project' % site_root):
  sys.path.append(p)

os.environ['DJANGO_SETTINGS_MODULE'] = 'project.settings'

# End Django bootstrap code

def getChangedFiles(path, revno):
  """
    Query svn repository for changes in a given changeset.

  """

  client = pysvn.Client()
  revision = pysvn.Revision(pysvn.opt_revision_kind.number, revno)
  previous = pysvn.Revision(pysvn.opt_revision_kind.number, int(revno)-1)
  changes = client.diff_summarize(
      url_or_path1 = path,
      revision1 = previous,
      revision2 = revision,
  )

  formatted = []

  if changes:
    for c in changes:
      # Create a list of two-tuples, containing the svn 'action'
      # (add, delete, modify, etc.) and the file that's been acted upon.
      formatted.append(('%s' % c.summarize_kind, '%s' % c.path))

  return tuple(formatted)

if __name__ == '__main__':

  # We should get something like 
  # /path/to/repository <revision number>
  # i.e., "/var/svn/sites/apps 46"
  #
  # argv[0], of course, is our script name.

  assert len(sys.argv) == 3

  # Django imports

  from django.core.mail import EmailMessage
  from django.template import Context, Template
  from django.conf import settings
  from django.contrib.sites.models import Site

  # Load the svn client
  client = pysvn.Client()
  
  # Create a "proper" svn url
  repo_url = 'file://%s' % sys.argv[1] 

  # Get our revision number.
  rev = sys.argv[2]

  # Send our url and the revision number to getChangedFiles
  changes = getChangedFiles(repo_url, rev)

  # Only act if there have been changes.
  if changes:
    # Attempt to ascertain which site this is for.
    site = Site.objects.all(pk=settings.SITE_ID)

    # Get the 'from' email setting from settings.py
    from_email = settings.DEFAULT_FROM_EMAIL

    # I'm pulling from 'MANAGERS', which by default is set from
    # 'ADMINS', but it could just as easily pull from a database
    # query.

    admin_list = [mail_tuple[1] for mail_tuple in settings.MANAGERS]

    # Get the commit metadata - author, commit date, etc.
    log = client.log(repo_url,
                    revision_start = pysvn.Revision(pysvn.opt_revision_kind.head),
                    revision_end=pysvn.Revision(pysvn.opt_revision_kind.number, 
                    int(rev)))[0]

    # Convert the float timestamp into a UTC datetime object.
    stamp = datetime.utcfromtimestamp(log['date'])

    # Further convert it into something more human readable.
    friendly_date = stamp.strftime('%A %b %d, %Y')
    friendly_time = stamp.strftime('%I:%M %P')

    # Get the author / committer.
    author = log['author']

    # Get the commit message
    message = log['message']

    # Build our template context.
    c = Context(dict(
      site = site,
      author = author,
      date = friendly_date,
      time = friendly_time,
      divider = '-' * 67,
      revision = rev,
      changes = changes,
      message = message,
    ))

    # Create our subject and body templates. We can also use normal
    # templates for this, but that goes beyond the context of this
    # snippet.

    s = Template("Commit for {{ site }}")
    b = Template("""Revision: {{ revision }}
Author: {{ author }}
Date: {{ date }} {{ time }}
{{ divider }}
{{ message|default:"(no commit message)" }}
{% for action, path in changes %}
{{ action|capfirst }} {{ path }}{% endfor %}
""")

    # Render our templates.
    subject = s.render(c)
    body = b.render(c)

    # Set up our email message.
    mail = EmailMessage(subject, body, from_email, admin_list)

    # Send using text. Change to 'html' to use rich formatting.
    mail.content_subtype = 'plain'

    # Send our commit information!
    mail.send()