################################################################################
#Program Name - similarity_graph.py	
#							  
#Programmer Name - Sachin Jaiswal (CS12B1033)
#			  
#Description - Python script that takes "folder name" and value of "alpha"
#			   and "beta" from user and generate required html & Java script 
#			   file and visualization of graph can be seen using "index.html"
#			   generated by this script
#			  
#Execution - Include "munkres" package in Python library as described by 
#		 	 "Including munkres" and then run "similarity_graph.py"
################################################################################

# importing all the required packages
import os
import sys
import re
import colorsys
from munkres import Munkres, print_matrix, make_cost_matrix
import Tkinter, tkFileDialog

# for displaying window to select the folder from directories 
root = Tkinter.Tk()
root.withdraw()
mypath = tkFileDialog.askdirectory(parent=root,initialdir=".",title='Please select a directory')

# forming array of files in the given folder
file = os.listdir(mypath)
number_file = len(file)

# defining the global variables for program	
alpha=0.0
beta=0.0
total=0
P=0	
N = 0
no = 1
S = [[0.0 for x in range(number_file)] for y in range(number_file)]

# for displaying window to take values of 'alpha' & 'beta' from user
class Values(Tkinter.Tk):
    # default constructor
    def __init__(self, parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()
	# defining the layout of the window to input values
    def initialize(self):
        self.grid()
        stepOne = Tkinter.LabelFrame(self, text="---------------Enter Values---------------")
		# giving dimension for the window to input values
        stepOne.grid(row=0, columnspan=7, sticky='W',padx=7, pady=7, ipadx=7, ipady=7)
        self.Val1Lbl = Tkinter.Label(stepOne,text="Value of Alpha")
        self.Val1Lbl.grid(row=0, column=0, sticky='E', padx=5, pady=2)
        self.Val1Txt = Tkinter.Entry(stepOne)
        self.Val1Txt.grid(row=0, column=1, columnspan=3, pady=2, sticky='WE')
        self.Val2Lbl = Tkinter.Label(stepOne,text="  Value of Beta")
        self.Val2Lbl.grid(row=1, column=0, sticky='E', padx=5, pady=2)
        self.Val2Txt = Tkinter.Entry(stepOne)
        self.Val2Txt.grid(row=1, column=1, columnspan=3, pady=2, sticky='WE')
        self.val1 = None
        self.val2 = None
		# forming submit button
        SubmitBtn = Tkinter.Button(stepOne, text="Submit",command=self.submit)
        SubmitBtn.grid(row=4, column=3, sticky='W', padx=5, pady=2)
	# creating the window for taking values
    def submit(self):
        self.val1=self.Val1Txt.get()
        if self.val1=="":
            Win2=Tkinter.Tk()
            Win2.withdraw()
        self.val2=self.Val2Txt.get()
        if self.val2=="":
            Win2=Tkinter.Tk()
            Win2.withdraw()
        self.quit()
# making the main to call function of class	
if __name__ == '__main__':
    app = Values(None)
    app.title('Values for Program')
    app.mainloop() #this will run until it closes

print "Application is running..."	
# storing the values given by user	
alpha=float(app.val1)
beta=float(app.val2)

# function to return all the lines of argument file in a set
def file_line(path):
	set_list = []
	# opening a file to get lines
	file_obj = open(path)
	for line_data in file_obj:
		# splitting each line in separate word list 
		token = line_data.split()
		word_set = set()
		for words in token:
			# checking only for space as a delimiter and for alphanumeric characters 
			words = re.sub(r'\W+', '', words)
			# converting each word in lower case
			word_set.add(words.lower())
		# storing the set of words of each line
		set_list.append(word_set)
	return set_list

# function to return a similarity array of lines between the two argument files 
def comparison(list_current,list_called):
	similarity = [[] for i in range(len(list_current))]
	i = 0
	# getting lines of first file
	for line_current in list_current:
		j = 0
		# getting lines of second file
		for line_called in list_called:
			# checking for empty line
			if len(set.union(line_current, line_called))>0:
				# getting value of similarity between two lines by intersection by union of it
				similarity[i].append(float(len(set.intersection(line_current, line_called)))/float(len(set.union(line_current, line_called))))
				# checking from the value of alpha
				if similarity[i][j]<alpha:
					similarity[i][j]=0.0
			else:
				similarity[i].append(0.0)
			j=j+1
		i=i+1
	# returning the list of similar lines between two files 
	return similarity
	
# function to maximum weighted bipartite matching on the bipartite graph of similarity between two files
def similar(list_current,list_called):
	global P,total
	# comparing the length of two files to compare smaller length to bigger 
	if len(list_current)<len(list_called):
		# calling comparison function to compare both files line by line for similarity 
		similarity = comparison(list_current,list_called)
		# storing the lenght of smaller 
		P = len(list_current)
		point=[[0 for x in range(len(list_called))] for y in range(len(list_current))]
	else:
		# calling comparison function to compare both files line by line for similarity
		similarity = comparison(list_called,list_current)
		P = len(list_called)
		point=[[0 for x in range(len(list_current))] for y in range(len(list_called))]
	# calling functions of munkres to form maximum weighted bipartite matching graph
	graph_matrix = make_cost_matrix(similarity, lambda cost: 1.0 - cost)
	m = Munkres()
	indexes =m.compute(graph_matrix)
	total = 0
	for row, column in indexes:
		# forming list of points(lines) of similarity between two files
		value = similarity[row][column]
		if value>0.0:
			total += 1
			point[row][column]=1
	return point
	
# generating Java script file containing list of vertices(file) and edges(similarity between files)
# creating file to write data
outfile = open("similarity.js","w")
outfile.write("var graph={" + '\n\t'+"\"nodes\":["+'\n')
for i in range(number_file-1):
	# writing names of every file which will act as the vertex in the graph 
	outfile.write('\t'+'\t'+"{\"name\":\""+file[i]+"\",\"number\":"+str(i)+"},"+'\n')
outfile.write('\t'+'\t'+"{\"name\":\""+file[number_file-1]+"\",\"number\":"+str(number_file-1)+"}"+'\n\t'+"],"+'\n\t'+"\"links\":["+'\n')
# writing data to interpret the edges between the vertex  
for i in range(number_file):
	# getting the set of words of every line in a file
	list_current = file_line(mypath+"/"+file[i])
	for j in range(i+1,number_file):
		# getting the set of words of every line in a file
		list_called = file_line(mypath+"/"+file[j])
		# calling similar function to get the list of lines similar in two files
		points=similar(list_current,list_called)
		# calculating the similarity of two files
		S[i][j] = float(total)/float(P)
		if S[i][j] >= beta:
			# checking the similarity to give the edge between the files
			if (len(list_current)<len(list_called)):
				outfile.write('\t'+'\t'+"{\"source\":"+str(i)+",\"target\":"+str(j)+",\"value\":"+str(S[i][j])+",\"pair\":[")
			else:
				outfile.write('\t'+'\t'+"{\"source\":"+str(j)+",\"target\":"+str(i)+",\"value\":"+str(S[i][j])+",\"pair\":[")
			# putting the list of line numbers similar in two file between whose edge is formed 
			for x in range(len(points)):
				for y in range(len(points[x])):
					if points[x][y]==1:
						outfile.write("["+str(x)+","+str(y)+"],")
						N = N+1
			# giving unique colour to each pair of lines
			outfile.write("],\"color\":")
			HSV_tuples = [(x*1.0/N, 0.5, 0.5) for x in range(N)]
			RGB_tuples = map(lambda x: colorsys.hsv_to_rgb(*x), HSV_tuples)
			RGB_tuples = map(lambda x: tuple(map(lambda y: int(y * 255),x)),RGB_tuples)
			HEX_tuples = map(lambda x: tuple(map(lambda y: chr(y).encode('hex'),x)), RGB_tuples)
			HEX_tuples = map(lambda x: "".join(x), HEX_tuples)			
			outfile.write(str(HEX_tuples)+",},"+'\n')
			N=0
outfile.write('\t'+"]"+"\n}")
outfile.close()

# generating Java script file storing the content of every file
# creating file to write
f_write = open("file_data.js","w")
f_write.write("var data={" + '\n\t'+"\"text\":["+'\n')
for i in range(number_file):
	# reading contents of each file
	f_read = open(mypath+"/"+file[i],"r")
	f_write.write('\t'+'\t'+"{\"content\":\"")
	for j in f_read:
		# escaping the character
		j=j.replace('"',"'")
		j=j.replace('<','&lt')
		j=j.replace('>','&gt')
		# giving unique id to each line for colouring the similar file while displaying  
		f_write.write("<p id=\\\"line"+str(no-1)+"\\\">"+str(no)+": "+str(j).strip()+"<br></p>")
		no = no+1
	no = 1
	f_read.close()
	f_write.write("\"},"+'\n')
f_write.write('\t'+"]"+"\n}")
f_write.close()

# generating CSS file
f_write = open("style.css","w")
f_write.write(".node\n{\n\tstroke: #fff;\n\tstroke-width: 1.5px;\n\tfill: #01A9DB;\n}\n")
f_write.write(".node:hover\n{\n\tfill: red;\n}\n")
f_write.write(".link\n{\n\tstroke: #000;\n\tstroke-opacity: .6;\n}\n")
f_write.write(".svg\n{\n\tposition: absolute;\n\ttop:0;\n\tbottom:0;\n\tright:0;\n\tleft:0;\n\tmargin:auto;\n\tborder-style:double;\n\tborder-color:red;\n\tbackground-color:#E5E4E2;\n}\n")
f_write.write(".container \n{\n\tposition: absolute;\n\ttop:0;\n\tbottom:0;\n\tright:0;\n\tleft:0;\n\tmargin:auto;\n\tborder-style:double;\n\tborder-color:black;\n\tbackground-color: white;\n\tdisplay:none;\n\tz-index:2;\n}\n")
f_write.write(".name\n{\n\ttext-align:center;\n\tcolor:red;\n\tfont-size:20px;\n\tmargin-top:20px;\n\ttext-decoration:underline;\n}\n")
f_write.write(".close\n{\n\tfloat:right;\n\tmargin-right:10px;4\n\tfont-size:20px;\n\tcolor:red;\n}\n")
f_write.write(".content\n{\n\tmargin-top:15px;\n\tmargin-left:15px;\n\tborder-style:dotted;\n\tborder-color:green;\n\toverflow-y: scroll;\n\toverflow-x: scroll;\n}\n")
f_write.close()

# generating Java Script file to draw the graph of similarity between files
f_write = open("graph.js","w")
f_write.write("var vis, width, height, svg, colour, link, node, force;\n\n")
f_write.write("function start()\n{\n\twidth = 960;\n\theight = 500;\n\n\tcolor = d3.scale.category20();\n\n\tforce = d3.layout.force()\n\t\t.charge(-120)\n\t\t.linkDistance(30)\n\t\t.size([width, height]);\n\n")
f_write.write("\tvis = d3.select(\"body\").append(\"svg\")\n\t\t.attr(\"width\", width)\n\t\t.attr(\"height\", height)\n\t\t.attr(\"class\", \"svg\")\n\t\t.call(d3.behavior.zoom().on(\"zoom\", redraw));\n\n\tsvg = vis\n\t\t.append('svg:g');\n\n\tforce\n\t\t.nodes(graph.nodes)\n\t\t.links(graph.links)\n\t\t.start();\n\n")
f_write.write("\tlink = svg.selectAll(\".link\")\n\t\t.data(graph.links)\n\t\t.enter().append(\"line\")\n\t\t.attr(\"class\", \"link\")\n\t\t.style(\"stroke-width\", function(d) { return d.value*10; })\n\t\t.on(\"click\",function(d){")
f_write.write("\n\t\t\tc=d3.select(\"#two_file\").style(\"display\",\"block\");\n\t\t\td3.select(\"#one_file\").style(\"display\",\"none\");\n\t\t\tc.select(\"#file_name1\").html(d.source.name);\n\t\t\tc.select(\"#file_name2\").html(d.target.name);\n\t\t\tone=c.select(\"#file_data1\").html(data.text[d.source.number].content);\n\t\t\ttwo=c.select(\"#file_data2\").html(data.text[d.target.number].content);\n\t\t\tfor (var i=0; i<d.pair.length; i++)\n\t\t\t{one.select(\"#line\"+d.pair[i][0]).style(\"background-color\",\"#\"+d.color[i]);\n\t\t\ttwo.select(\"#line\"+d.pair[i][1]).style(\"background-color\",\"#\"+d.color[i]);}});")
f_write.write("\n\n\tnode = svg.selectAll(\".node\")\n\t\t.data(graph.nodes)\n\t\t.enter().append(\"circle\")\n\t\t.attr(\"class\", \"node\")\n\t\t.attr(\"r\", 5)\n\t\t.call(force.drag)\n\t\t.on(\"mouseover\",function(){vis.call(d3.behavior.zoom().on(\"zoom\",function(){}))})\n\t\t.on(\"mouseout\",function(){vis.call(d3.behavior.zoom().on(\"zoom\",redraw))})\n\t\t.on(\"click\",function(d){")
f_write.write("\n\t\t\tc=d3.select(\"#one_file\").style(\"display\",\"block\");\n\t\t\td3.select(\"#two_file\").style(\"display\",\"none\");\n\t\t\tc.select(\"#file_name\").html(d.name);c.select(\"#file_data\").html(data.text[d.number].content);});")
f_write.write("\n\n\tnode.append(\"title\")\n\t\t.text(function(d) { return d.name; });\n\n\tlink.append(\"title\")\n\t\t.text(function(d) { return d.source.name +\" & \"+ d.target.name+\" with similarity \"+d.value });")
f_write.write("\n\n\tforce.on(\"tick\", function() {\n\t\tlink.attr(\"x1\", function(d) { return d.source.x; })\n\t\t.attr(\"y1\", function(d) { return d.source.y; })\n\t\t.attr(\"x2\", function(d) { return d.target.x; })\n\t\t.attr(\"y2\", function(d) { return d.target.y; });\n\n\tnode.attr(\"cx\", function(d) { return d.x; })\n\t\t.attr(\"cy\", function(d) { return d.y; });});\n}")
f_write.write("\nfunction redraw()\n{\n\tsvg.attr(\"transform\",\"translate(\" + d3.event.translate + \")\"+ \" scale(\" + d3.event.scale + \")\");\n}\nfunction close1()\n{\n\td3.select('#one_file').style('display','none');\n}\nfunction close2() \n{\n\td3.select('#two_file').style('display','none');\n}")
f_write.close()

# generating html file for displaying the graph of similarity between files
f_write = open("index.html","w")
f_write.write("<!DOCTYPE html>\n<html>\n<head>\n<title>Similarity Graph</title>\n<meta charset=\"utf-8\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n<script type=\"text/javascript\" src=\"d3.v3.min.js\"></script>\n<script type=\"text/javascript\" src=\"similarity.js\"></script>\n<script type=\"text/javascript\" src=\"file_data.js\"></script>\n<script type=\"text/javascript\" src=\"graph.js\"></script>\n</head>")
f_write.write("\n<body onload=\"start()\">\n<div class=\"container\" id=\"one_file\" style=\"width:79%;height:85%\">\n\t<div class=\"close\" onclick=\"close1()\">\n\t\t<p>[X]</p>\n\t</div>\n\t<div class=\"name\" id=\"file_name\">\n\t</div>\n\t<div class=\"content\" id=\"file_data\"style=\"height:85%;width:96%;\">\n\t</div>\n</div>")
f_write.write("\n<div class=\"container\" id=\"two_file\" style=\"width:79%;height:85%\">\n\t<div class=\"close\" onclick=\"close2()\">\n\t\t<p>[X]</p>\n\t</div>\n\t<div class=\"name\" id=\"file_name1\" style=\"width:35%;display:inline-block;float:left;\">\n\t</div>\n\t<div class=\"name\" id=\"file_name2\" style=\"width:50%;float:right;\">\n\t</div>\n\t<div class=\"content\" id=\"file_data1\" style=\"width:39.5%;height:85%;display:inline-block;float:left;\">\n\t</div>\n\t<div class=\"content\" id=\"file_data2\" style=\"width:39.5%;height:85%;float:right;\">\n\t</div>\n</div>\n</body>\n</html>")	
f_write.close()			
	
print "Application execution successful!"	
		