#!/usr/bin/env python
#
# svn_summary
# Summarize the state of SVN repositories
#

import sys
import os
import commands
import re

def print_alert(type,file,alert):
    if type == "OK":
       return
    print "  <"+type+">"
    print "    <File>"+file+"</File>"
    print "    <Alert>"+alert+"</Alert>"
    print "  </"+type+">"

def find_externals(dir,prefix=""):
  res = []
  for name in os.listdir(dir):
    if os.path.isdir(dir+"/"+name):
       res = res + find_externals(dir+"/"+name, prefix+"/"+name)
    else:
       if name == "Externals": 
          res.append(prefix + "/" + name)
  return res 


def guess_projroot(external):
    tokens = external.split("/")
    num = len(tokens)
    i = num-1
    while i>=0:
      if (tokens[i] == "trunk") or (tokens[i] == "releases") or (tokens[i] == "stable") or (tokens[i] == "branches") or (tokens[i] == "tags"):
         break
      i = i-1
    if i == -1:
       return "/".join(tokens)
    return "/".join(tokens[0:i])


def guess_release(repos,name,subdir):
    output = commands.getoutput("svn ls " + repos+name+"/"+subdir)
    if output=="":
       return (None,None)
    id = (0,0,0)
    for dir in output.split("\n"):
       tmp = tuple(dir[-1].split("."))
       if tmp > id:
	  release=dir[:-1]
    if name != "":
       name =name+"/"
    #print repos,name,subdir,release
    return (release,repos+name+subdir+"/"+release)


class SVNProject:
  def __init__(self,repos,name):
	self.has_trunk=False
	self.has_releases=False
	self.has_tags=False
	self.has_branches=False
	self.has_stable=False

	self.stable_name = None
	self.stable_diffs = None
	self.stable_dir = None

	self.release_name = None
	self.release_diffs = None
	self.release_dir = None

	self.tag_name = None
	self.tag_diffs = None
	self.tag_dir = None

	self.repos=repos
	self.name=name
	if self.name is None:
	   self.name_str = ""
	else:
	   self.name_str = "/" + name 
	self.proj_root = repos + self.name_str
	subdir = commands.getoutput("svn ls " + repos+self.name_str)
	self.dirs = subdir.split("\n")
	self.init(self.dirs)

  def init(self,dlist):
	#
	# Get layout
	#
	self.has_trunk = "trunk/" in dlist
	self.has_releases = "releases/" in dlist
	self.has_tags = "tags/" in dlist
	self.has_branches = "branches/" in dlist
	self.has_stable = "stable/" in dlist
	#
	# Guess release and stable version
	#
	if self.has_releases:
           (self.release_name,self.release_dir) = guess_release(self.repos,self.name_str,"releases")
	if self.has_tags:
           (self.tag_name,self.tag_dir) = guess_release(self.repos,self.name_str,"tags")
	if self.has_stable:
           (self.stable_name,self.stable_dir) = guess_release(self.repos,self.name_str,"stable")
	#
	# If have a trunk and stable directory, then summarize the number
	# of differences.
	#
	if self.has_trunk and self.stable_name is not None:
	   output = commands.getoutput("svn diff --summarize " + self.repos+self.name_str+"/trunk " + self.repos+self.name_str+"/stable/"+self.stable_name)
	   if output == "":
	      self.stable_diffs = 0
	   else:
	      diffs = output.split("\n")
	      self.stable_diffs = len(diffs)
	#
	# If have a trunk and a release directory, then summarize the number
	# of differences.
	#
	if self.has_trunk and self.release_name is not None:
	   output = commands.getoutput("svn diff --summarize " + self.repos+self.name_str+"/trunk " + self.release_name)
	   if output == "":
	      self.release_diffs = 0
	   else:
	      diffs = output.split("\n")
	      self.release_diffs = len(diffs)
	#
	# If have a trunk and a tag directory, then summarize the number
	# of differences.
	#
	if self.has_trunk and self.tag_name is not None:
	   output = commands.getoutput("svn diff --summarize " + self.repos+self.name_str+"/trunk " + self.tag_name)
	   if output == "":
	      self.tag_diffs = 0
	   else:
	      diffs = output.split("\n")
	      self.tag_diffs = len(diffs)
	   

  def write(self,format=""):
	if format=="text":
	   print ""
	   print "  Project         ",self.name
	   print "  Project Root    ",self.proj_root
	   print "  Trunk Branch    ",self.has_trunk
	   print "  Normal Branches ",self.has_branches

	   print "  Tag Branches    ",self.has_tags
	   print "    Latest Tag    ",self.tag_name
	   print "    Tag Diffs     ",self.tag_diffs
	   print "    Tag Path      ",self.tag_dir

	   print "  Stable Branches ",self.has_stable
	   print "    Latest Stable   ",self.stable_name
	   print "    Stable Diffs    ",self.stable_diffs
	   print "    Stable Path     ",self.stable_dir

	   print "  Release Branches",self.has_releases
	   print "    Latest Release  ",self.release_name
	   print "    Release Diffs   ",self.release_diffs
	   print "    Release Path    ",self.release_dir
	elif format=="xml":
	   print "  <Project>"
	   if self.name is None:
	      print "    <Name>" + "" + "</Name>"
	   else:
	      print "    <Name>" + self.name + "</Name>"
	   print "    <TrunkBranch>"+`self.has_trunk`+"</TrunkBranch>"
	   print "    <TagBranches>"+`self.has_tags`+"</TagBranches>"
	   print "    <NormalBranches>"+`self.has_branches`+"</NormalBranches>"

	   print "    <StableBranches>"+`self.has_stable`+"</StableBranches>"
	   print "    <LatestStable>"+self.stable_name+"</LatestStable>"
	   print "    <StableDiffs>"+`self.stable_diffs`+"</StableDiffs>"
	   if self.stable_dir is None:
	      spath="None"
	   else:
	      spath = self.stable_dir
	   print "    <StablePath>"+spath+"</StablePath>"

	   print "    <ReleaseBranch>"+`self.has_releases`+"</ReleaseBranch>"
	   print "    <LatestRelease>"+self.release_name+"</LatestRelease>"
	   print "    <ReleaseDiffs>"+`self.release_diffs`+"</ReleaseDiffs>"
	   if self.release_dir is None:
	      rpath="None"
	   else:
	      rpath = self.release_dir
	   print "    <ReleasePath>"+rpath+"</ReleasePath>"

	   print "  </Project>"


##
## MAIN
##
if len(sys.argv) == 1:
   print ""
   print "A tool to summarize the state of SVN repositories"
   print ""
   print "svn_summary text <svn-repository> [...]"
   print "  Provide a plain-text summary"
   print ""
   print "svn_summary xml <svn-repository> [...]"
   print "  Provide an XML textual summary"
   print ""
   print "svn_summary [--aux=<repos>,...,<repos>] alerts <svn-repository> [...]"
   print "  Provide alterts for svn externals that appear missing or out of date"
   print ""
   sys.exit(1)

offset=0
auxsvn=[]
if sys.argv[1][:6] == "--aux=":
   offset=1
   tmp=(sys.argv[1])[6:]
   auxsvn = tmp.split(",")
start=2+offset
format=sys.argv[1+offset]

#
# Create a dictionary of Subversion projects and their corresponding release directories
#
svn_projects = {}
dirs = sys.argv[start:] + auxsvn
for repos in dirs:
  if format=="xml":
     print "<Repository>"
     print "  <Name>"+repos+"</Name>"
  elif format=="text":
     print "REPOSITORY",repos
  dir = commands.getoutput("svn ls " + repos)
  dirs = dir.split("\n")
  has_trunk = "trunk/" in dirs
  if has_trunk:
     proj = SVNProject(repos,None)
     svn_projects[ (repos,proj.name) ] = proj
     proj.write(format)
  else:
     for file in dir.split("\n"):
       file = file.strip()
       if file[-1] == "/":
          proj = SVNProject(repos,file[:-1])
          svn_projects[ proj.proj_root ] = proj
          proj.write(format)
  if format=="xml":
     print "</Repository>"
  elif format == "text":
     print ""

if format != "alerts":
   sys.exit(0)

#
# Scan for externals, and verify whether they are for the latest releases
#
print "<ExternalsAlerts>"
for repos in sys.argv[start:]:
  #print "Alerts for repository",repos,"..."
  cmd="rm -Rf /tmp/svn_repos; svn checkout -q --ignore-externals " + repos + " /tmp/svn_repos"
  commands.getoutput(cmd)
  odir = "/tmp/svn_repos"
  externals = find_externals(odir)
  if len(externals) > 0:
     for external in externals:
       FILE = open(odir + "/" + external,"r")
       #print " Examining file " + external
       for line in FILE:
         link = re.split('[ \t]+',line.strip())[1]
         #print "  LINK: " + link
         #print "  ROOT: " + guess_projroot(link)
         projroot = guess_projroot(link)
         if projroot not in svn_projects.keys():
            print_alert("Error",repos + "/" + external,"Project root not found: " + projroot)
         else:
	    proj = svn_projects[ projroot ]
	    tokens = link.split("/")
	    i = len(tokens)-1
            while i>=0:
              if (tokens[i] == "trunk") or (tokens[i] == "releases") or (tokens[i] == "stable") or (tokens[i] == "branches") or (tokens[i] == "tags"):
                 break
              i = i-1
	    if tokens[i] == "trunk":
	       if not proj.has_trunk:
                  print_alert("Error",repos + "/" + external,"External missing: " + link)
	       else:
                  print_alert("OK",repos + "/" + external,"External OK: " + link)
	    elif tokens[i] == "branches":
	       if not proj.has_branches:
                  print_alert("Error",repos + "/" + external,"External missing: " + link)
	       else:
                  print_alert("Warning",repos + "/" + external,"External not verified: " + link)
	    elif tokens[i] == "tags":
	       if not proj.has_tags:
                  print_alert("Error",repos + "/" + external,"External missing: " + link)
	       elif link != proj.tag_dir:
                  print_alert("LinkUpdate",repos + "/" + external,"Update for link " + link + " : " + proj.tag_dir)
	       else:
                  print_alert("OK",repos + "/" + external,"External OK: " + link)
	    elif tokens[i] == "releases":
	       if not proj.has_releases:
                  print_alert("Error",repos + "/" + external,"External missing: " + link)
	       elif link != proj.release_dir:
                  print_alert("LinkUpdate",repos + "/" + external,"Update for link " + link + " : " + proj.release_dir)
	       else:
                  print_alert("OK",repos + "/" + external,"External OK: " + link)
	    elif tokens[i] == "stable":
	       if not proj.has_stable:
                  print_alert("Error",repos + "/" + external,"External missing: " + link)
	       elif link != proj.stable_dir:
                  print_alert("LinkUpdate",repos + "/" + external,"Update for link " + link + " : " + proj.stable_dir)
	       else:
                  print_alert("OK",repos + "/" + external,"External OK: " + link)
	    else:
               print_alert("NonStandard",repos + "/" + external,"External not verified: " + link)
  else:
     print "  None"
print "</ExternalsAlerts>"

