view — bibtex_project 1.0 documentation

# Source code for view

    #!/usr/bin/env python
    import Tkinter as tk
    from Tkinter import *
    import controller
    import os
    from git import *
    from git import Repo
    import shutil
    import Tkinter, Tkconstants, tkFileDialog
    # from pyparsing import Each

    class View(tk.Frame):
        """ ..class:: View
                This class is used for getting user inputs. It uses the tkinter GUI package.
                ..data::master_file
                    StringVar() variable holds the name of the master file
                ..data::local_file
                    StringVar() variable holds the name of the local file
                ..data::master_path
                    StringVar() variable holds the name of the directory which holds the master file
                ..data::local_path
                    StringVar() variable holds the name of the directory which holds the local file
        """
        def __init__(self, *args, **kwargs):
            """This method creates a window for the user to select whether his files are on disk or git"""
            tk.Frame.__init__(self, *args, **kwargs)
            self.option = IntVar()
            self.master_file = StringVar()
            self.local_file = StringVar()
            self.master_path = os.getcwd()+"/"+"testing_01"
            self.local_path = os.getcwd()+"/"+"testing_02"
            Radiobutton(self, text = "Files on Disk",variable = self.option,value = 1).grid(row = 0, column = 1)
            Radiobutton(self, text = "Files on Git",variable = self.option, value = 2).grid(row = 1, column = 1)
            ok_button = Button(self,text = "Submit",command = self.choose_option)
            ok_button.grid(columnspan=2)

        def openmasterfile(self,event):
            """method is used to select a file from the local hard disk"""
            self.master_file.set(tkFileDialog.askopenfilename())

        def openlocalfile(self,event):
            """method is used to select a file from the local hard disk"""
            self.local_file.set(tkFileDialog.askopenfilename())

        def choose_option(self):
            """check for the option user selected and help the user select his files"""
            if(self.option.get()==1):
                self.select_disk_files()
            else:
                self.select_git_files()

        def select_disk_files(self):
            """create a new window to input the two repositories from which the two bibtex files will be selected"""
            def onFrameConfigure(canvas):
        #'''Reset the scroll region to encompass the inner frame'''
                canvas.configure(scrollregion=canvas.bbox("all"))
            new_window = tk.Toplevel(self)
            canvas = tk.Canvas(new_window, borderwidth=0, background="#ffffff")
            window = tk.Frame(canvas, background="#ffffff")
            vsb = tk.Scrollbar(new_window, orient="vertical", command=canvas.yview)
            hsb = tk.Scrollbar(new_window, orient="horizontal", command=canvas.xview)
            canvas.configure(xscrollcommand = hsb.set,yscrollcommand=vsb.set)
            vsb.pack(side="right", fill="y")
            hsb.pack(side="top", fill="x")
            canvas.pack(side="left", fill="both", expand=True)
            canvas.create_window((4,4), window=window, anchor="nw")
            window.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))
            label_1 = Label(window, text = "Local File")
            label_2 = Label(window, text = "Master File")
            master_file_entry = Entry(window, textvariable=self.master_file)
            master_file_entry.bind("<Button-1>", self.openmasterfile)
            local_file_entry = Entry(window, textvariable=self.local_file)
            local_file_entry.bind("<Button-1>", self.openlocalfile)
            label_1.grid(row = 0,sticky=E)
            label_2.grid(row = 1,sticky=E)
            master_file_entry.grid(row = 0, column = 1)
            local_file_entry.grid(row=1,column=1)
            ok_button = Button(window,text = "Submit",command = self.call_controller)
            ok_button.grid(columnspan=2)

        def select_git_files(self):
            """creates a new window and accepts the names of the two git repositories to be used by the application"""
            master_repo = StringVar()
            local_repo = StringVar()
            branch_name_master = StringVar()
            branch_name_local = StringVar()
            def onFrameConfigure(canvas):
                '''Reset the scroll region to encompass the inner frame'''
                canvas.configure(scrollregion=canvas.bbox("all"))
            new_window = tk.Toplevel(self)
            canvas = tk.Canvas(new_window, borderwidth=0, background="#ffffff")
            window = tk.Frame(canvas, background="#ffffff")
            vsb = tk.Scrollbar(new_window, orient="vertical", command=canvas.yview)
            hsb = tk.Scrollbar(new_window, orient="horizontal", command=canvas.xview)
            canvas.configure(xscrollcommand = hsb.set,yscrollcommand=vsb.set)
            vsb.pack(side="right", fill="y")
            hsb.pack(side="top", fill="x")
            canvas.pack(side="left", fill="both", expand=True)
            canvas.create_window((4,4), window=window, anchor="nw")
            window.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))
            label_1 = Label(window, text = "Git Repo 1")
            label_2 = Label(window, text = "Git Repo 2")
            label_3 = Label(window, text = "Branch_Name")
            label_4 = Label(window, text = "Branch_Name")
            r1 = Entry(window, textvariable=master_repo)
            br_master = Entry(window, textvariable=branch_name_master)
            r2 = Entry(window, textvariable=local_repo)
            br_local = Entry(window, textvariable=branch_name_local)
            text = master_repo.get()
            master_repo.set(text)
            text = local_repo.get()
            local_repo.set(text)
            label_1.grid(row = 0,sticky=E)
            label_4.grid(row = 1,sticky=E)
            label_2.grid(row = 2,sticky=E)
            label_3.grid(row = 3,sticky=E)
            r1.grid(row = 0, column = 1)
            br_master.grid(row=1,column=1)
            r2.grid(row=2,column=1)
            br_local.grid(row=3,column=1)
            ok_button = Button(window,text = "Submit",command =lambda: self.select_files(master_repo.get(),local_repo.get(),branch_name_master.get(),branch_name_local.get()))
            ok_button.grid(columnspan=2)

        def select_files(self,repo1,repo2,branch1,branch2):
            """given the names of two repositories and their branch names this method creates a local repository and pulls down the content from git"""
            if(os.path.exists(self.master_path)):
                 shutil.rmtree(self.master_path)
            self.repo_master = create_local_repo(repo1,self.master_path,branch1)
            if(os.path.exists(self.local_path)):
                 shutil.rmtree(self.local_path)
            self.repo_local = create_local_repo(repo2,self.local_path,branch2)
            self.repo_master.remotes.origin.pull()
            self.repo_local.remotes.origin.pull()
            self.select_rb_files()

        def select_rb_files(self):
            """creates a window which list the different bibtex files present in the local directories of both the master and local repositories """
            def onFrameConfigure(canvas):
                '''Reset the scroll region to encompass the inner frame'''
                canvas.configure(scrollregion=canvas.bbox("all"))
            new_window = tk.Toplevel(self)
            canvas = tk.Canvas(new_window, borderwidth=0, background="#ffffff")
            window = tk.Frame(canvas, background="#ffffff")
            vsb = tk.Scrollbar(new_window, orient="vertical", command=canvas.yview)
            hsb = tk.Scrollbar(new_window, orient="horizontal", command=canvas.xview)
            canvas.configure(xscrollcommand = hsb.set,yscrollcommand=vsb.set)
            vsb.pack(side="right", fill="y")
            hsb.pack(side="top", fill="x")
            canvas.pack(side="left", fill="both", expand=True)
            canvas.create_window((4,4), window=window, anchor="nw")
            window.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))
            self.list_master_repo = extract_bib_files(self.master_path)
            self.list_local_repo = extract_bib_files(self.local_path)
            line = 0            #line variable holds the current row number on the gui for our grid formatting
            if(len(self.list_master_repo)>0):
                Label(window,text="Select the master file",font=("Times", 16),foreground="Blue").grid(row = line+1,columnspan=2,sticky=W,padx=2,pady=2)
                for idx,val in enumerate(self.list_master_repo):
                    line = line+1
                    Radiobutton(window, text=self.list_master_repo[idx],variable=self.master_file,value = self.list_master_repo[idx]).grid(row=line+1,column=0,sticky=W,padx=1,pady=1)
            line = line+2
            if(len(self.list_local_repo)>0):
                Label(window,text="Select the local file",font=("Times", 16),foreground="Blue").grid(row = line+1,columnspan=2,sticky=W,padx=1,pady=1)
                for idx,val in enumerate(self.list_local_repo):
                    line = line+1
                    Radiobutton(window, text=self.list_local_repo[idx],variable=self.local_file,value = self.list_local_repo[idx]).grid(row=line+1,column=0,sticky=W,padx=1,pady=1)
            Button(window, text="Done", command=self.call_controller).grid(columnspan=2,sticky=W,padx=1,pady=1)

        def list_differences(self,list,list2):
            """create a window to display the differences between the two files as two categories: one shows the differences between values of
                the two properties for a record and the other shows the property present in the master file but not in the local file
                Args:
                    list: list of record that have a difference in properties on the master file and the local file
                    list2:list of record that have a property on the master file which is not present on the local file
            """
            def onFrameConfigure(canvas):
                '''Reset the scroll region to encompass the inner frame'''
                canvas.configure(scrollregion=canvas.bbox("all"))
            new_window = tk.Toplevel(self)
            canvas = tk.Canvas(new_window, borderwidth=0, background="#ffffff")
            window = tk.Frame(canvas, background="#ffffff")
            vsb = tk.Scrollbar(new_window, orient="vertical", command=canvas.yview)
            hsb = tk.Scrollbar(new_window, orient="horizontal", command=canvas.xview)
            canvas.configure(xscrollcommand = hsb.set,yscrollcommand=vsb.set)
            vsb.pack(side="right", fill="y")
            hsb.pack(side="top", fill="x")
            canvas.pack(side="left", fill="both", expand=True)
            canvas.create_window((4,4), window=window, anchor="nw")
            window.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))
            line = 0
            if(len(list)>0):
                current_elem = list[0][0]
                Label(window,text="There is a difference in the below properties for the master and local file",font=("Times", 16),foreground="Blue").grid(columnspan=2,sticky=W,padx=2,pady=2)
                for idx,val in enumerate(list):
                    line = line+1
                    new_elem = list[idx][0]
                    if(new_elem!=current_elem or idx==0):
                        Label(window,text="ID:"+current_elem,font=("Times", 14,"bold")).grid(row=line+1,sticky=W,padx=1,pady=1)
                        current_elem=new_elem
                        line = line +1
                    Label(window,text=list[idx][1][0]+":").grid(row=line+1,column=0,sticky=W,padx=1,pady=1)
                    Label(window, text=list[idx][1][1]).grid(row=line+1,column=1,sticky=W,padx=1,pady=1)
                    Label(window,text=list[idx][2][0]+":").grid(row=line+1,column=2,sticky=W,padx=1,pady=1)
                    Radiobutton(window, text=list[idx][2][1],variable=list[idx][2][3],value = 1).grid(row=line+1,column=3,sticky=W,padx=1,pady=1)
            line = line+2
            if(len(list2)>0):
                Label(window,text="Below property is not present in the local file",font=("Times", 16),foreground="Blue").grid(row = line,columnspan=2,sticky=W,padx=1,pady=1)
                Label(window,text="Select properties you want to add",font=("Times", 16),foreground="Blue").grid(row = line+1,columnspan=2,sticky=W,padx=1,pady=1)
                line = line +2
                current_elem = list2[0][0]
                for idx,val in enumerate(list2):
                    line = line+1
                    new_elem = list2[idx][0]
                    if(new_elem!=current_elem or idx==0):
                        Label(window,text="ID:"+current_elem,font =("Times", 14,"bold")).grid(row=line+1,columnspan=2,sticky=W,padx=1,pady=1)
                        current_elem=new_elem
                        line = line+1
                    Label(window,text=list2[idx][1][0]+":").grid(row=line+1,sticky=W,padx=1,pady=1)
                    Radiobutton(window, text=list2[idx][1][1],variable=list2[idx][1][3],value = 1).grid(row=line+1,column=1,sticky=W,padx=1,pady=1)
            self.update = True
            if(len(list)==0 and len(list2)==0):
                Label(window,text="No differences between the two files",font=("Times", 16),foreground="Blue").grid(row = line,columnspan=2,sticky=W,padx=1,pady=1)
                self.update = False
            Button(window, text="Done", command=lambda:self.controller.update(self.update,list,list2)).grid(columnspan=2,sticky=W,padx=1,pady=1)

        def call_controller(self):
            """Create the controller instance """
            self.controller = controller.Controller(self)

        def close(self):
            """deletes the instance of View , commits the new added features to the local file to the remote repository and deletes all the temporary directories
                that were created for this application """
            if(self.option.get()==2):
                commit_remote(self.repo_local,self.local_file.get())
                shutil.rmtree(self.master_path)
                shutil.rmtree(self.local_path)
            self.destroy()
            root_close()

    def commit_remote(repo,file):
        """ Commits to the index of the local git repository and calls the git push
            Args:
                repo: Reference to the head of the local git repository
                file: File that will be added to the index for the commit and push
                    to the remote git repository
            Raises:
                git.exc.GitCommandError: If git.push() fails
        """

        try:
            repo.index.add([file])
            commit = repo.index.commit("Modified File"+file)

            for each in repo.heads:
                branch = each

            merge_base = repo.merge_base(repo.remotes.origin,branch)
            repo.index.merge_tree(branch,base=merge_base)
            repo.remotes.origin.push()

        except git.exc.GitCommandError:
            print "Git Push Error"

    #outside
    def create_local_repo(remote_git,dir,branch):
        """ Uses the git.clone_from method to clone a remote git repository locally
            Args:
                remote_git: Url of the remote git repository
                dir: Local directory where the contents of the
                    remote git will be downloaded
                branch: Name of the branch on the remote git which will be used for cloning
            Returns:
                git.repo: Reference to the local git repository
            Raises:
                git.exc.InvalidGitRepositoryError: If remote git repository is bare
                git.exc.GitCommandError: If remote git repository does not exist
        """

        if(os.path.exists(dir)):
             shutil.rmtree(dir)
        try:
            repo = Repo.clone_from(
                    url=remote_git,
                    to_path=dir,
                    branch=branch
                    )
            if repo.bare:
                raise git.exc.InvalidGitRepositoryError
            else:
                return repo

        except git.exc.GitCommandError:
            print "Please make sure you have the correct access rights and the repository exists"

    def extract_bib_files(path):
        """
            Extracts all the bib files from the given path
            Args:
                path : The directory name from which bib files will be extracted
            Returns:
                List[string]: Returns a list of bib files present in path
        """
        list = []
        for root, dirs, files in os.walk(path):
            for name in files:
                if(os.path.join(root, name)).endswith(".bib"):
                    list.append(((os.path.join(root, name))))
            for name in dirs:
                if(os.path.join(root, name)).endswith(".bib"):
                    list.append(((os.path.join(root, name))))
        return list

    if __name__ == "__main__":
        root = tk.Tk()
        view = View(root)
        view.pack(side="top", fill="both", expand=True)
        def root_close():
            root.destroy()

        root.mainloop()