Browse Source

Ignores functionality enhanced

Akash Singh 3 years ago
parent
commit
4acc8bad45
4 changed files with 80 additions and 20 deletions
  1. 38 8
      CloneXls/ChangeLog.py
  2. 6 3
      CloneXls/__init__.py
  3. 21 1
      README.md
  4. 15 8
      tests/test_CloneXls.py

+ 38 - 8
CloneXls/ChangeLog.py

@@ -24,6 +24,7 @@ class ChangeLog:
         self.ignore_sheets.append(self.log_sheet_name)
         self.ignore_sheets.append(self.log_sheet_name)
         self.case_senstive_ignore = case_sensitive_ignore
         self.case_senstive_ignore = case_sensitive_ignore
         self.update_ignore_list()
         self.update_ignore_list()
+        self.headers_column = {}
 
 
     def update_ignore_list(self):
     def update_ignore_list(self):
         # Making data inside igonre lists in lower only if case_sensitive_ignore is false, so that it will be later
         # Making data inside igonre lists in lower only if case_sensitive_ignore is false, so that it will be later
@@ -48,9 +49,10 @@ class ChangeLog:
         self.check_log_sheet(clone_sheets, clone)
         self.check_log_sheet(clone_sheets, clone)
         sheets = self.get_sheets(source_sheets, clone_sheets)
         sheets = self.get_sheets(source_sheets, clone_sheets)
         logs = self.get_change_logs(sheets, source, clone)
         logs = self.get_change_logs(sheets, source, clone)
-        self.update_change_logs(logs, clone)
-        self.update_clone_file(source, clone, source_sheets)
-        clone.save(self.clone_file)
+        if logs:  # if their is no update in the non ignored data than file won't be updated
+            self.update_change_logs(logs, clone)
+            self.update_clone_file(source, clone, source_sheets)
+            clone.save(self.clone_file)
         clone.close()
         clone.close()
         source.close()
         source.close()
 
 
@@ -127,11 +129,12 @@ class ChangeLog:
         # for change log. If the sheet already present in clone file then we delete it and create a new one with exact
         # for change log. If the sheet already present in clone file then we delete it and create a new one with exact
         # data from source
         # data from source
         for sheet in source_sheets:
         for sheet in source_sheets:
-            try:  # checking if the sheet exists in clone file, if it does than we first take its index than delete it
+            if sheet in clone.sheetnames:  # checking if the sheet exists in clone file, if it does than we first take its index than delete it
                 clone_sht = clone.get_sheet_by_name(sheet)
                 clone_sht = clone.get_sheet_by_name(sheet)
                 ind = clone.index(clone_sht)
                 ind = clone.index(clone_sht)
+                ignored_headers_data = self.get_ignored_data_from_clone(clone, sheet)  # ignored data from clone file
                 clone.remove(clone_sht)
                 clone.remove(clone_sht)
-            except:  # if clone file doesn't has that sheet than make the index containing variable to "False"
+            else:  # if clone file doesn't has that sheet than make the index containing variable to "False"
                 ind = "False"  # A string is taken instead of any empty value because the original index can contain 0
                 ind = "False"  # A string is taken instead of any empty value because the original index can contain 0
                                # which can cause conflict
                                # which can cause conflict
             if ind != "False":  # if we have an index value than make a new sheet in that position
             if ind != "False":  # if we have an index value than make a new sheet in that position
@@ -140,11 +143,20 @@ class ChangeLog:
                 clone.create_sheet(sheet, len(clone.sheetnames)-2)
                 clone.create_sheet(sheet, len(clone.sheetnames)-2)
             source_sht = source.get_sheet_by_name(sheet)
             source_sht = source.get_sheet_by_name(sheet)
             clone_sht = clone.get_sheet_by_name(sheet)
             clone_sht = clone.get_sheet_by_name(sheet)
+            skipped = 0
             for row in range(1, source_sht.max_row+1):  # looping through every row from source
             for row in range(1, source_sht.max_row+1):  # looping through every row from source
                 for column in range(1, source_sht.max_column+1):  # looping through every column from source
                 for column in range(1, source_sht.max_column+1):  # looping through every column from source
-                    clone_sht.cell(row, column).value = source_sht.cell(row, column).value  #writing everything in clone
-                    if source_sht.cell(row, column).has_style:  # if source cell has style than copy it to clone
-                        clone_sht.cell(row, column)._style = copy(source_sht.cell(row, column)._style)
+                    if sheet in self.headers_column:
+                        if column-1 in self.headers_column[sheet]:
+                            if row == 1:  # if first row then write header
+                                clone_sht.cell(row, column).value = self.headers_column[sheet][column-1]
+                            elif row > 1:  # write data from the ignore data list, column-1 coz the header was index from 0 starting point
+                                clone_sht.cell(row, column).value = ignored_headers_data[
+                                    self.headers_column[sheet][column-1]][row-2]  # row-2 because here it starts from 2 and list starts from 0
+                            continue
+                    clone_sht.cell(row, column).value = source_sht.cell(row, column-skipped).value  #writing everything in clone
+                    if source_sht.cell(row, column-skipped).has_style:  # if source cell has style than copy it to clone
+                        clone_sht.cell(row, column-skipped)._style = copy(source_sht.cell(row, column-skipped)._style)
             for idx, rd in source_sht.row_dimensions.items():  # copying width and height of rows and columns
             for idx, rd in source_sht.row_dimensions.items():  # copying width and height of rows and columns
                 clone_sht.row_dimensions[idx] = copy(rd)
                 clone_sht.row_dimensions[idx] = copy(rd)
 
 
@@ -183,6 +195,7 @@ class ChangeLog:
         # columns. Logic is same like get_sheets method
         # columns. Logic is same like get_sheets method
         source_headers = [cell.value for cell in source_sht[1]]
         source_headers = [cell.value for cell in source_sht[1]]
         clone_headers = [cell.value for cell in clone_sht[1]]
         clone_headers = [cell.value for cell in clone_sht[1]]
+        self.headers_column[source_sht.title] = {}
         headers = []
         headers = []
         temp_headers = []
         temp_headers = []
         new_headers = []
         new_headers = []
@@ -192,8 +205,12 @@ class ChangeLog:
                 if self.case_senstive_ignore:
                 if self.case_senstive_ignore:
                     if header not in self.ignore_headers:
                     if header not in self.ignore_headers:
                         temp_headers.append(header)
                         temp_headers.append(header)
+                    else:
+                        self.headers_column[source_sht.title][source_headers.index(header)] = header
                 elif header.lower() not in self.ignore_headers:
                 elif header.lower() not in self.ignore_headers:
                     temp_headers.append(header)
                     temp_headers.append(header)
+                else:
+                    self.headers_column[source_sht.title][source_headers.index(header)] = header
             else:
             else:
                 temp_headers.append(header)
                 temp_headers.append(header)
 
 
@@ -215,3 +232,16 @@ class ChangeLog:
                 if header not in source_headers:
                 if header not in source_headers:
                     removed_headers.append(header)
                     removed_headers.append(header)
         return headers, new_headers, removed_headers
         return headers, new_headers, removed_headers
+
+    def get_ignored_data_from_clone(self, clone, sheet):
+        data = {}
+        sht = clone.get_sheet_by_name(sheet)
+        headers = [cell.value for cell in sht[1]]
+        if not sheet in self.headers_column:
+            return data
+        for header in self.headers_column[sheet]:
+            hd = self.headers_column[sheet][header]  # getting header from class list storing index of ignored headers
+            data[hd] = []
+            for i in range(2, sht.max_row+1):
+                data[hd].append(sht.cell(i, headers.index(hd)+1).value)
+        return data

+ 6 - 3
CloneXls/__init__.py

@@ -18,7 +18,8 @@ class CloneXls:
         if not os.path.exists(self.source_file):
         if not os.path.exists(self.source_file):
             raise BaseException(f"{self.source_file} doesn't exists, please verify the path entered!")
             raise BaseException(f"{self.source_file} doesn't exists, please verify the path entered!")
 
 
-    def update_or_make_clone(self):
+    def update_or_make_clone(self, log_sheet_name="Change Logs", ignore_headers=[], ignore_sheets=[],
+            case_sensitive_ignore=False, clone=True):
         """
         """
         This method is used to update the clone file with the change log and if their is no clone file then make one
         This method is used to update the clone file with the change log and if their is no clone file then make one
         :return:
         :return:
@@ -30,8 +31,10 @@ class CloneXls:
                 data = file.read()
                 data = file.read()
             with open(self.clone_file, "wb") as file:
             with open(self.clone_file, "wb") as file:
                 file.write(data)
                 file.write(data)
-        else:
-            changeLog = ChangeLog(self.source_file, self.clone_file)
+        elif clone:
+            changeLog = ChangeLog(self.source_file, self.clone_file,
+                                  log_sheet_name=log_sheet_name, ignore_headers=ignore_headers,
+                                  ignore_sheets=ignore_sheets, case_sensitive_ignore=case_sensitive_ignore)
             changeLog.xlsxChangeLog()
             changeLog.xlsxChangeLog()
         # Returning filename with absolute path of clone file
         # Returning filename with absolute path of clone file
         return self.clone_file
         return self.clone_file

+ 21 - 1
README.md

@@ -1,3 +1,23 @@
 # baangt-CloneXLS
 # baangt-CloneXLS
 
 
-Clone a base XLS before it is used. Update any changes and write change log into cloned version
+This package was mainly made to use in [baangt](https://www.baangt.org/). 
+
+It's functionality is to Clone a ``excel file(xls/xlsx)`` and a new sheet is made inside cloned file which is used to 
+store the change logs. 
+
+This package contains two main classes. First is ``CloneXls`` & second is ``ChangeLog``.
+
+CloneXls
+========
+``update_or_make_clone`` is the main method of this class. On the first run it will create a clone file and if the file 
+already exist it will call ``ChangeLog`` class.
+
+ChangeLog
+=========
+This class is used to check the changes between source and cloned file and update those changes inside cloned file.
+Their are few helpful functionalities like ignore_headers & ignore_sheets these both parameters takes a list.
+Headers and sheets present in this lists will be ignored for change log and will not be updated from source.
+``xlsxChangeLog`` is the main method of this class which will trigger all the other methods like checking for changes,
+updating change log sheet, updating whole clone file from source(except ignored data). AIt will write all the 
+changes in ``Change Logs`` worksheet. This data will contain 
+``Date & time, "Sheet Name", "Header Name", "Row Number", "Old Value", "New Value"``.

+ 15 - 8
tests/test_CloneXls.py

@@ -24,10 +24,10 @@ with open(Path(input_directory).joinpath(original_file), "rb") as file:
     data = file.read()
     data = file.read()
 
 
 with open(Path(input_directory).joinpath(input_file), "wb") as file:
 with open(Path(input_directory).joinpath(input_file), "wb") as file:
-    file.write(data)
+    file.write(data)  # creating a duplicate file as we will update the source file in the run for test to update change log
 
 
 
 
-def update_source(path):
+def update_source(path):  # update the file which is used to check the changelog
     wb = load_workbook(path)
     wb = load_workbook(path)
     sht = wb.get_sheet_by_name(sheet)
     sht = wb.get_sheet_by_name(sheet)
     for row in range(1, sht.max_row+1):
     for row in range(1, sht.max_row+1):
@@ -39,29 +39,32 @@ def update_source(path):
     wb.save(path)
     wb.save(path)
 
 
 
 
-def check_source(path, ignore=False):
+def check_source(path, case_sensitive=False):
+    # checks the change log sheet that if their is value added and verifies it by row number in which we have changed data
     wb = load_workbook(path)
     wb = load_workbook(path)
     sht = wb.get_sheet_by_name("Change Logs")
     sht = wb.get_sheet_by_name("Change Logs")
-    if not ignore:
+    if case_sensitive:  # if case_sensitive test is done these 3 changes will the only ones
         assert int(sht.cell(2, 4).value) == 1
         assert int(sht.cell(2, 4).value) == 1
         assert int(sht.cell(3, 4).value) == 1
         assert int(sht.cell(3, 4).value) == 1
         assert int(sht.cell(4, 4).value) == 8
         assert int(sht.cell(4, 4).value) == 8
         assert sht.max_row == 4
         assert sht.max_row == 4
-    else:
+    else:  # if case_sensitive test is done these 2 changes will the only ones
         assert int(sht.cell(2, 4).value) == 1
         assert int(sht.cell(2, 4).value) == 1
         assert int(sht.cell(3, 4).value) == 8
         assert int(sht.cell(3, 4).value) == 8
         assert sht.max_row == 3
         assert sht.max_row == 3
 
 
 
 
 def test_check_source_file():
 def test_check_source_file():
+    # Give a fake file path so it must throw a BaseException
     try:
     try:
-        CloneXls.CloneXls("tests/TestInput/CompleteBaangtWebdemo1.xlsx")
+        CloneXls.CloneXls("tests/TestInput/CompleteBaangtWebdemo_Fake.xlsx")
         assert 1 == 0
         assert 1 == 0
     except BaseException:
     except BaseException:
         assert 1 == 1
         assert 1 == 1
 
 
 
 
 def test_update_or_make_clone():
 def test_update_or_make_clone():
+    # Checking update_or_make_clone method of CloneXls class, that will it make a clone file if their is none
     output = str(Path(input_directory).joinpath(output_file))
     output = str(Path(input_directory).joinpath(output_file))
     input_ = str(Path(input_directory).joinpath(input_file))
     input_ = str(Path(input_directory).joinpath(input_file))
     if os.path.exists(output):
     if os.path.exists(output):
@@ -72,6 +75,7 @@ def test_update_or_make_clone():
 
 
 
 
 def test_update_or_make_clone_prefix():
 def test_update_or_make_clone_prefix():
+    # testing the change prefix parameter
     output = str(Path(input_directory).joinpath(output_file_2))
     output = str(Path(input_directory).joinpath(output_file_2))
     input = str(Path(input_directory).joinpath(input_file))
     input = str(Path(input_directory).joinpath(input_file))
     if os.path.exists(output):
     if os.path.exists(output):
@@ -83,6 +87,7 @@ def test_update_or_make_clone_prefix():
 
 
 
 
 def test_ChangeLog_class():
 def test_ChangeLog_class():
+    # creating a ChangeLog class object and verifies that if it is build successfully
     output = str(Path(input_directory).joinpath(output_file))
     output = str(Path(input_directory).joinpath(output_file))
     input_ = str(Path(input_directory).joinpath(input_file))
     input_ = str(Path(input_directory).joinpath(input_file))
     changeLog = ChangeLog(input_, output)
     changeLog = ChangeLog(input_, output)
@@ -90,6 +95,7 @@ def test_ChangeLog_class():
 
 
 
 
 def test_ChangeLog_not_case_sensitive():
 def test_ChangeLog_not_case_sensitive():
+    # Testing ChangeLog class with ignore lists treated as non case sensitive
     output = str(Path(input_directory).joinpath(output_file))
     output = str(Path(input_directory).joinpath(output_file))
     input_ = str(Path(input_directory).joinpath(input_file))
     input_ = str(Path(input_directory).joinpath(input_file))
     changeLog = ChangeLog(
     changeLog = ChangeLog(
@@ -99,10 +105,11 @@ def test_ChangeLog_not_case_sensitive():
            changeLog.ignore_sheets[0].islower()
            changeLog.ignore_sheets[0].islower()
     update_source(input_)
     update_source(input_)
     changeLog.xlsxChangeLog()
     changeLog.xlsxChangeLog()
-    check_source(output, ignore=True)
+    check_source(output)
 
 
 
 
 def test_ChangeLog_case_sensitive():
 def test_ChangeLog_case_sensitive():
+    # Testing ChangeLog class with ignore lists treated as case sensitive
     output = str(Path(input_directory).joinpath(output_file))
     output = str(Path(input_directory).joinpath(output_file))
     input_ = str(Path(input_directory).joinpath(input_file))
     input_ = str(Path(input_directory).joinpath(input_file))
     os.remove(output)
     os.remove(output)
@@ -116,7 +123,7 @@ def test_ChangeLog_case_sensitive():
     update_source(input_)
     update_source(input_)
     changeLog.xlsxChangeLog()
     changeLog.xlsxChangeLog()
     changeLog.xlsxChangeLog()
     changeLog.xlsxChangeLog()
-    check_source(output)
+    check_source(output, case_sensitive=True)
     os.remove(input_)
     os.remove(input_)
     os.remove(output)
     os.remove(output)