DataComparerLibrary 0.845__tar.gz → 0.846__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/PKG-INFO +3 -3
  2. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/setup.py +0 -1
  3. datacomparerlibrary-0.846/src/DataComparerLibrary/datacomparer.py +162 -0
  4. datacomparerlibrary-0.846/src/DataComparerLibrary/field.py +166 -0
  5. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/src/DataComparerLibrary/report.py +16 -10
  6. datacomparerlibrary-0.846/src/DataComparerLibrary/row.py +32 -0
  7. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/src/DataComparerLibrary/tools.py +2 -1
  8. datacomparerlibrary-0.846/src/DataComparerLibrary/twodarray.py +36 -0
  9. datacomparerlibrary-0.846/src/DataComparerLibrary/version.py +3 -0
  10. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/src/DataComparerLibrary.egg-info/PKG-INFO +3 -3
  11. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/src/DataComparerLibrary.egg-info/SOURCES.txt +4 -3
  12. datacomparerlibrary-0.845/src/DataComparerLibrary/arraycomparer.py +0 -206
  13. datacomparerlibrary-0.845/src/DataComparerLibrary/datacomparer.py +0 -135
  14. datacomparerlibrary-0.845/src/DataComparerLibrary/version.py +0 -3
  15. datacomparerlibrary-0.845/test/test1.py +0 -21
  16. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/LICENSE.txt +0 -0
  17. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/README.rst +0 -0
  18. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/pyproject.toml +0 -0
  19. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/setup.cfg +0 -0
  20. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/src/DataComparerLibrary/__init__.py +0 -0
  21. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/src/DataComparerLibrary/datasorter.py +0 -0
  22. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/src/DataComparerLibrary/datetimehandler.py +0 -0
  23. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/src/DataComparerLibrary/delimitertranslator.py +0 -0
  24. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/src/DataComparerLibrary/fileconverter.py +0 -0
  25. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/src/DataComparerLibrary.egg-info/dependency_links.txt +0 -0
  26. {datacomparerlibrary-0.845 → datacomparerlibrary-0.846}/src/DataComparerLibrary.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: DataComparerLibrary
3
- Version: 0.845
3
+ Version: 0.846
4
4
  Summary: For comparing csv-files, 2d-array with a csv-file or 2d-arrays. For comparing text-files, text variable with a text-file or text variables. Including a sorting module.
5
5
  Home-page:
6
6
  Author: René Philip Zuijderduijn
@@ -10,7 +10,6 @@ Keywords: robotframework testing test-automation datacompare
10
10
  Classifier: License :: OSI Approved :: Apache Software License
11
11
  Classifier: Operating System :: Microsoft :: Windows :: Windows 10
12
12
  Classifier: Operating System :: Microsoft :: Windows :: Windows 11
13
- Classifier: Programming Language :: Python :: 3.7
14
13
  Classifier: Programming Language :: Python :: 3.8
15
14
  Classifier: Programming Language :: Python :: 3.9
16
15
  Classifier: Programming Language :: Python :: 3.10
@@ -26,6 +25,7 @@ Dynamic: description
26
25
  Dynamic: description-content-type
27
26
  Dynamic: keywords
28
27
  Dynamic: license
28
+ Dynamic: license-file
29
29
  Dynamic: summary
30
30
 
31
31
  ===================
@@ -33,7 +33,6 @@ def main():
33
33
  "License :: OSI Approved :: Apache Software License",
34
34
  "Operating System :: Microsoft :: Windows :: Windows 10",
35
35
  "Operating System :: Microsoft :: Windows :: Windows 11",
36
- "Programming Language :: Python :: 3.7",
37
36
  "Programming Language :: Python :: 3.8",
38
37
  "Programming Language :: Python :: 3.9",
39
38
  "Programming Language :: Python :: 3.10",
@@ -0,0 +1,162 @@
1
+ # Script for comparing csv-files, 2d-array with a csv-file or 2d-arrays and for comparing text-files, text variable with a text-file or text variables.
2
+ #
3
+ import csv
4
+ import os
5
+
6
+ from DataComparerLibrary.twodarray import TwoDArray
7
+ from DataComparerLibrary.report import Report
8
+
9
+
10
+ class DataComparer:
11
+ def compare_data_2d_array_with_file(self, actual_data, expected_file, delimiter_expected_data=",", quotechar_expected_data='"', template_literals_dict=None):
12
+ self.__check_if_actual_data_is_present(actual_data)
13
+ self.__check_if_expected_file_is_present(expected_file)
14
+ #
15
+ expected_data = self.__get_data_from_csv_input_file(expected_file, delimiter_expected_data, quotechar_expected_data)
16
+ #
17
+ DataComparer.compare_data(actual_data, expected_data, template_literals_dict)
18
+
19
+
20
+ def compare_data_file_with_2d_array(self, actual_file, expected_data, delimiter_actual_data=",", quotechar_actual_data='"', template_literals_dict=None):
21
+ self.__check_if_actual_file_is_present(actual_file)
22
+ self.__check_if_expected_data_is_present(expected_data)
23
+ #
24
+ actual_data = self.__get_data_from_csv_input_file(actual_file, delimiter_actual_data, quotechar_actual_data)
25
+ #
26
+ DataComparer.compare_data(actual_data, expected_data, template_literals_dict)
27
+
28
+
29
+ def compare_data_2d_arrays(self, actual_data, expected_data, template_literals_dict=None):
30
+ self.__check_if_actual_data_is_present(actual_data)
31
+ self.__check_if_expected_data_is_present(expected_data)
32
+ #
33
+ DataComparer.compare_data(actual_data, expected_data, template_literals_dict)
34
+
35
+
36
+ def compare_data_files(self, actual_file, expected_file, delimiter_actual_data=",", delimiter_expected_data=",", quotechar_actual_data='"', quotechar_expected_data='"', template_literals_dict=None):
37
+ self.__check_if_actual_file_is_present(actual_file)
38
+ self.__check_if_expected_file_is_present(expected_file)
39
+ #
40
+ actual_data = self.__get_data_from_csv_input_file(actual_file, delimiter_actual_data, quotechar_actual_data)
41
+ expected_data = self.__get_data_from_csv_input_file(expected_file, delimiter_expected_data, quotechar_expected_data)
42
+ #
43
+ DataComparer.compare_data(actual_data, expected_data, template_literals_dict)
44
+
45
+
46
+
47
+ def compare_text_variable_with_text_file(self, actual_text, expected_file, template_literals_dict=None):
48
+ self.__check_if_actual_data_is_present(actual_text)
49
+ self.__check_if_expected_file_is_present(expected_file)
50
+ #
51
+ actual_data = self.__split_text_into_textline_array(actual_text)
52
+ expected_data = self.__split_textfile_into_textline_array(expected_file)
53
+ #
54
+ DataComparer.compare_data(actual_data, expected_data, template_literals_dict)
55
+
56
+
57
+ def compare_text_file_with_text_variable(self, actual_file, expected_text, template_literals_dict=None):
58
+ self.__check_if_actual_file_is_present(actual_file)
59
+ self.__check_if_expected_data_is_present(expected_text)
60
+ #
61
+ actual_data = self.__split_textfile_into_textline_array(actual_file)
62
+ expected_data = self.__split_text_into_textline_array(expected_text)
63
+ #
64
+ DataComparer.compare_data(actual_data, expected_data, template_literals_dict)
65
+
66
+
67
+ def compare_text_variables(self, actual_text, expected_text, template_literals_dict=None):
68
+ self.__check_if_actual_data_is_present(actual_text)
69
+ self.__check_if_expected_data_is_present(expected_text)
70
+ #
71
+ actual_data = self.__split_text_into_textline_array(actual_text)
72
+ expected_data = self.__split_text_into_textline_array(expected_text)
73
+ #
74
+ DataComparer.compare_data(actual_data, expected_data, template_literals_dict)
75
+
76
+
77
+ def compare_text_files(self, actual_file, expected_file, template_literals_dict=None):
78
+ self.__check_if_actual_file_is_present(actual_file)
79
+ self.__check_if_expected_file_is_present(expected_file)
80
+ #
81
+ actual_data = self.__split_textfile_into_textline_array(actual_file)
82
+ expected_data = self.__split_textfile_into_textline_array(expected_file)
83
+ #
84
+ DataComparer.compare_data(actual_data, expected_data, template_literals_dict)
85
+
86
+
87
+
88
+ @staticmethod
89
+ def compare_data(actual_data, expected_data, template_literals_dict=None):
90
+ actual_data = DataComparer.__make_data_2d_compliant(actual_data)
91
+ expected_data = DataComparer.__make_data_2d_compliant(expected_data)
92
+ #
93
+ Report.show_header_and_data("Actual data", actual_data)
94
+ Report.show_header_and_data("Expected data", expected_data)
95
+ Report.show_header_differences_actual_and_expected_data()
96
+ #
97
+ Report().show_footer_comparation_result(TwoDArray(actual_data).equals(TwoDArray(expected_data), template_literals_dict))
98
+
99
+
100
+ @staticmethod
101
+ def __check_if_actual_data_is_present(data):
102
+ if data == None:
103
+ raise Exception("Actual Input data unknown.")
104
+
105
+
106
+ @staticmethod
107
+ def __check_if_expected_data_is_present(data):
108
+ if data == None:
109
+ raise Exception("Expected Input data unknown.")
110
+
111
+
112
+ @staticmethod
113
+ def __check_if_actual_file_is_present(file):
114
+ if os.path.exists(file):
115
+ print("actual_file: ", file)
116
+ else:
117
+ raise Exception("Actual Input file doesn't exists: ", file)
118
+
119
+
120
+ @staticmethod
121
+ def __check_if_expected_file_is_present(file):
122
+ if os.path.exists(file):
123
+ print("expected_file: ", file)
124
+ else:
125
+ raise Exception("Expected Input file doesn't exists: ", file)
126
+
127
+
128
+ @staticmethod
129
+ def __get_data_from_csv_input_file(input_file, delimiter_data=",", quotechar_data='"'):
130
+ with open(input_file, mode='rt', encoding='utf-8') as input_file:
131
+ if len(delimiter_data) == 1:
132
+ data = list(csv.reader(input_file, delimiter=delimiter_data, quotechar=quotechar_data))
133
+ else:
134
+ data = list(csv.reader((line.replace(delimiter_data, chr(255)) for line in input_file), delimiter=chr(255), quotechar=quotechar_data))
135
+ #
136
+ return data
137
+
138
+
139
+ @staticmethod
140
+ def __split_text_into_textline_array(text):
141
+ data = []
142
+ for line in text.split('\n'):
143
+ data.append(line.strip('\n').split(chr(255)))
144
+ #
145
+ return data
146
+
147
+
148
+ @staticmethod
149
+ def __split_textfile_into_textline_array(input_file):
150
+ with open(input_file, mode='rt', encoding='utf-8') as input_file:
151
+ data = []
152
+ for line in input_file.readlines():
153
+ data.append(line.strip('\n').split(chr(255)))
154
+ #
155
+ return data
156
+
157
+
158
+ @staticmethod
159
+ def __make_data_2d_compliant(data):
160
+ if data and type(data[0]) is not list: # only a single row
161
+ data = [data,] # add row to tuple of lenght 1
162
+ return data
@@ -0,0 +1,166 @@
1
+ import fnmatch
2
+ import re
3
+
4
+ from DataComparerLibrary.datetimehandler import DatetimeHandler
5
+ from DataComparerLibrary.report import Report
6
+ from DataComparerLibrary.tools import Tools
7
+
8
+
9
+ class Field:
10
+ def __init__(self, field_data, row_nr, column_nr):
11
+ self.field_data = field_data
12
+ self.row_nr = row_nr
13
+ self.column_nr = column_nr
14
+
15
+
16
+ def equals(self, other_field_including_templates_and_literals, template_literals_dict):
17
+ equal = True
18
+
19
+ expected_data_including_date_template = None
20
+ expected_data_with_wildcard = None
21
+ skip_exception_rule_used = False
22
+
23
+ # Replace literal templates with fixed external strings.
24
+ other_field_data_including_templates = self.__replace_template_literals_dict(other_field_including_templates_and_literals.field_data, template_literals_dict)
25
+
26
+ if self.field_data == other_field_data_including_templates:
27
+ return True
28
+
29
+ # Verify if difference is a matter of string versus integer representation.
30
+ if Field.__is_same_value_but_different_types(self, other_field_data_including_templates):
31
+ return False
32
+
33
+ # If data in actual and expected field doesn't match, check if a template has been used in expected data.
34
+ match other_field_data_including_templates.upper():
35
+ case "{PRESENT}":
36
+ if not self.field_data:
37
+ # No data is present in actual data field.
38
+ equal = False
39
+ Report.show_differences_comparation_result(self.row_nr, self.column_nr, self.field_data, other_field_data_including_templates, "Actual data field is not PRESENT")
40
+ #
41
+ case "{EMPTY}":
42
+ if self.field_data:
43
+ # Actual data field is not empty.
44
+ equal = False
45
+ Report.show_differences_comparation_result(self.row_nr, self.column_nr, self.field_data, other_field_data_including_templates, "Actual data field is not EMPTY")
46
+ #
47
+ case "{INTEGER}":
48
+ if isinstance(self.field_data, int):
49
+ # A real integer.
50
+ return True
51
+ #
52
+ # Verify if string is integer.
53
+ if not self.field_data.isdigit():
54
+ # Not positive integer field.
55
+ equal = False
56
+ Report.show_differences_comparation_result(self.row_nr, self.column_nr, self.field_data, other_field_data_including_templates, "Actual data field is not INTEGER.")
57
+ #
58
+ case "{SKIP}":
59
+ pass
60
+ case _:
61
+ if "{SKIP}" in other_field_data_including_templates.upper() or "{DATETIME_FORMAT():YYYYMMDDHHMMSSFF6}" in other_field_data_including_templates.upper():
62
+ if other_field_data_including_templates.upper() == "{SKIP}":
63
+ # Complete actual data field will be skipped for verification.
64
+ pass
65
+ else:
66
+ # Part(s) of the actual data field will be skipped for verification.
67
+ # Replace {SKIP}, ignoring cases, by wildcard *.
68
+ # compiled = re.compile(re.escape("{SKIP}"), re.IGNORECASE)
69
+ # expected_data_with_wildcard = compiled.sub("*", other_field_data_including_templates)
70
+ compiled = re.compile(re.escape("{SKIP}"), re.IGNORECASE)
71
+ compiled2 = re.compile(re.escape("{DATETIME_FORMAT():YYYYMMDDHHMMSSFF6}"), re.IGNORECASE)
72
+ expected_data_with_wildcard = compiled2.sub("*", compiled.sub("*", other_field_data_including_templates))
73
+ #
74
+ if fnmatch.fnmatch(self.field_data, expected_data_with_wildcard):
75
+ skip_exception_rule_used = True
76
+ # continue
77
+ #
78
+ if expected_data_with_wildcard == None:
79
+ # Wildcards not used.
80
+ expected_data_including_date_template = other_field_data_including_templates
81
+ else:
82
+ expected_data_including_date_template = expected_data_with_wildcard
83
+ #
84
+ if "{NOW()" in other_field_data_including_templates.upper():
85
+ matches = ["{NOW():", "{NOW()+", "{NOW()-"]
86
+ if all([x not in other_field_data_including_templates.upper() for x in matches]):
87
+ equal = False
88
+ Report.show_differences_comparation_result(self.row_nr, self.column_nr, self.field_data, other_field_data_including_templates, "NOW() has been found in expected data field, but format is incorrect.")
89
+ #continue
90
+ #
91
+ expected_data = DatetimeHandler.replace_date_template_in_expected_data(self, expected_data_including_date_template)
92
+ #
93
+ if expected_data == -1:
94
+ equal = False
95
+ Report.show_differences_comparation_result(self.row_nr, self.column_nr, self.field_data, other_field_data_including_templates, "NOW() has been found in expected data field, but format is incorrect.")
96
+ else:
97
+ if not fnmatch.fnmatch(self.field_data, expected_data):
98
+ # No match despite using of wildcard(s).
99
+ equal = False
100
+ Report.show_differences_comparation_result(self.row_nr, self.column_nr, self.field_data, other_field_data_including_templates, "Date template format displayed. See also next message line.")
101
+ Report.show_differences_comparation_result(self.row_nr, self.column_nr, self.field_data, expected_data, "There is a difference between actual and expected data.")
102
+ # continue
103
+ #
104
+ elif "{NOT(" in other_field_data_including_templates.upper():
105
+ try:
106
+ unwanted_expected_data = Field.__get_unwanted_expected_data(expected_data_including_date_template)
107
+ #
108
+ if self.field_data == unwanted_expected_data:
109
+ # Unwanted match.
110
+ equal = False
111
+ Report.show_differences_comparation_result(self.row_nr, self.column_nr, self.field_data, other_field_data_including_templates, "NOT() template format displayed. See also next message line.")
112
+ Report.show_differences_comparation_result(self.row_nr, self.column_nr, self.field_data, unwanted_expected_data, "Actual and expected data are equal. However actual data should NOT be equal to the expected data!!!")
113
+ except Exception as exception_message:
114
+ # print(f"An exception occurred: {exception_message}")
115
+ equal = False
116
+ Report.show_differences_comparation_result(self.row_nr, self.column_nr, self.field_data, other_field_data_including_templates, "NOT() has been found in expected data field, but format is incorrect.")
117
+ #
118
+ else:
119
+ if not skip_exception_rule_used:
120
+ # No exceptions.
121
+ equal = False
122
+ Report.show_differences_comparation_result(self.row_nr, self.column_nr, self.field_data, other_field_data_including_templates, "There is a difference between actual and expected data. No exception rule has been used.")
123
+ #
124
+ return equal
125
+
126
+
127
+ @staticmethod
128
+ def __replace_template_literals_dict(data, template_literals_dict):
129
+ if template_literals_dict:
130
+ for i in range(0, len(template_literals_dict)):
131
+ data = data.replace(list(template_literals_dict.keys())[i], list(template_literals_dict.values())[i])
132
+ return data
133
+
134
+
135
+ def __is_same_value_but_different_types(self, other_field_data):
136
+ # Verify if difference is a matter of string versus integer representation.
137
+ if str(self.field_data) == str(other_field_data):
138
+ if isinstance(self.field_data, int) and isinstance(other_field_data, str):
139
+ Report.show_differences_comparation_result(self.row_nr, self.column_nr, self.field_data, other_field_data, "There is a difference between actual and expected data. Actual data is an integer while expected data is a string.")
140
+ return True
141
+ elif isinstance(self.field_data, str) and isinstance(other_field_data, int):
142
+ Report.show_differences_comparation_result(self.row_nr, self.column_nr, self.field_data, other_field_data, "There is a difference between actual and expected data. Actual data is a string while expected data is an integer.")
143
+ return True
144
+ return False
145
+
146
+
147
+ @staticmethod
148
+ def __get_unwanted_expected_data(expected_data_field_including_date_template):
149
+ position_open_brace = expected_data_field_including_date_template.find("{NOT(")
150
+ position_close_brace = expected_data_field_including_date_template.find(")}", position_open_brace)
151
+ #
152
+ if position_open_brace == -1:
153
+ #print("position_open_brace:", position_open_brace)
154
+ raise Exception()
155
+ #
156
+ if position_close_brace == -1:
157
+ #print("position_close_brace:", position_close_brace)
158
+ raise Exception()
159
+ #
160
+ unwanted_expected_data = expected_data_field_including_date_template[position_open_brace+5:position_close_brace]
161
+ #
162
+ if Tools.is_integer(unwanted_expected_data):
163
+ unwanted_expected_data = int(unwanted_expected_data)
164
+ return unwanted_expected_data
165
+
166
+
@@ -1,30 +1,36 @@
1
1
  class Report:
2
- def show_header_and_data(self, header, data):
3
- Report.show_2d_array(self, header, data, 20)
2
+ @staticmethod
3
+ def show_header_and_data(header, data):
4
+ Report.show_2d_array(header, data, 20)
4
5
 
5
6
 
6
- def show_header_differences_actual_and_expected_data(self):
7
+ @staticmethod
8
+ def show_header_differences_actual_and_expected_data():
7
9
  print()
8
10
  print("=== Overview differences between actual and expected data")
9
11
  print()
10
12
 
11
13
 
12
14
  #show_difference_between_actual_and_expected_data
13
- def show_differences_comparation_result(self, row_number, column_number, actual_data, expected_data, error_message):
15
+ @staticmethod
16
+ def show_differences_comparation_result(row_number, column_number, actual_data, expected_data, error_message):
14
17
  print("Row: ", row_number + 1, " Column: ", column_number + 1, " => Actual data: ", actual_data, " Expected data: ", expected_data, " Remark / Error message: ", error_message)
15
18
 
16
19
 
17
- #def show_footer(self, StatusMessage().difference):
18
- def show_footer_comparation_result(self):
19
- if StatusMessage().difference:
20
+ @staticmethod
21
+ def show_footer_comparation_result(equal):
22
+ if equal:
20
23
  print("\n\n\n")
21
- raise Exception("There is a difference between actual and expected data. See detail information.")
22
- else:
23
24
  print("There are no differences between actual and expected data found.")
24
25
  print("\n\n\n")
26
+ else:
27
+ print("\n\n\n")
28
+ raise Exception("There is a difference between actual and expected data. See detail information.")
29
+
25
30
 
26
31
 
27
- def show_2d_array(self, title, reader_file_list, column_width):
32
+ @staticmethod
33
+ def show_2d_array(title, reader_file_list, column_width):
28
34
  max_length_title = 30
29
35
  title = title[0:(max_length_title - 1)]
30
36
  length_title = len(title)
@@ -0,0 +1,32 @@
1
+ from DataComparerLibrary.field import Field
2
+ from DataComparerLibrary.report import Report
3
+
4
+
5
+ class Row:
6
+ def __init__(self, row_data, row_nr):
7
+ self.row_data = row_data
8
+ self.row_nr = row_nr
9
+ #
10
+ self.number_of_columns = len(row_data)
11
+
12
+
13
+ def equals(self, other_row, template_literals_dict):
14
+ equal = True
15
+ #
16
+ max_number_of_columns = max(self.number_of_columns, other_row.number_of_columns)
17
+ #
18
+ for column_nr in range(max_number_of_columns):
19
+ if column_nr >= self.number_of_columns:
20
+ equal = False
21
+ Report.show_differences_comparation_result(self.row_nr, column_nr, "", other_row.row_data[column_nr], "Column actual data is not PRESENT.")
22
+ continue
23
+ #
24
+ if column_nr >= other_row.number_of_columns:
25
+ equal = False
26
+ Report.show_differences_comparation_result(self.row_nr, column_nr, self.row_data[column_nr], "", "Column expected data is not PRESENT.")
27
+ continue
28
+ #
29
+ if not Field(self.row_data[column_nr], self.row_nr, column_nr).equals(Field(other_row.row_data[column_nr], self.row_nr, column_nr), template_literals_dict):
30
+ equal = False
31
+ #
32
+ return equal
@@ -1,5 +1,6 @@
1
1
  class Tools:
2
- def is_integer(self, string):
2
+ @staticmethod
3
+ def is_integer(string):
3
4
  if string[0] == '-':
4
5
  # if a negative number
5
6
  return string[1:].isdigit()
@@ -0,0 +1,36 @@
1
+ from DataComparerLibrary.row import Row
2
+ from DataComparerLibrary.report import Report
3
+
4
+
5
+ class TwoDArray:
6
+ def __init__(self, two_d_array_data):
7
+ self.two_d_array_data = two_d_array_data
8
+ self.number_of_rows = len(two_d_array_data)
9
+
10
+
11
+ def equals(self, other_two_d_array, template_literals_dict):
12
+ equal = True
13
+ #
14
+ max_number_of_rows = max(self.number_of_rows, other_two_d_array.number_of_rows)
15
+ #
16
+ for row_nr in range(max_number_of_rows):
17
+ if row_nr >= self.number_of_rows:
18
+ equal = False
19
+ if len(other_two_d_array.two_d_array_data[row_nr]) == 0:
20
+ Report.show_differences_comparation_result(row_nr, 0, "", "", "Row actual data is not PRESENT. Row expected data is EMPTY.")
21
+ else:
22
+ Report.show_differences_comparation_result(row_nr, 0, "", other_two_d_array.two_d_array_data[row_nr][0], "Row actual data is not PRESENT.")
23
+ continue
24
+ #
25
+ if row_nr >= other_two_d_array.number_of_rows:
26
+ equal = False
27
+ if len(self.two_d_array_data[row_nr]) == 0:
28
+ Report.show_differences_comparation_result(row_nr, 0, "", "", "Row actual data is EMPTY. Row expected data is not PRESENT.")
29
+ else:
30
+ Report.show_differences_comparation_result(row_nr, 0, self.two_d_array_data[row_nr][0], "", "Row expected data is not PRESENT.")
31
+ continue
32
+ #
33
+ if not Row(self.two_d_array_data[row_nr], row_nr).equals(Row(other_two_d_array.two_d_array_data[row_nr], row_nr), template_literals_dict):
34
+ equal = False
35
+ #
36
+ return equal
@@ -0,0 +1,3 @@
1
+ # encoding=utf-8
2
+
3
+ VERSION = '0.846'
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: DataComparerLibrary
3
- Version: 0.845
3
+ Version: 0.846
4
4
  Summary: For comparing csv-files, 2d-array with a csv-file or 2d-arrays. For comparing text-files, text variable with a text-file or text variables. Including a sorting module.
5
5
  Home-page:
6
6
  Author: René Philip Zuijderduijn
@@ -10,7 +10,6 @@ Keywords: robotframework testing test-automation datacompare
10
10
  Classifier: License :: OSI Approved :: Apache Software License
11
11
  Classifier: Operating System :: Microsoft :: Windows :: Windows 10
12
12
  Classifier: Operating System :: Microsoft :: Windows :: Windows 11
13
- Classifier: Programming Language :: Python :: 3.7
14
13
  Classifier: Programming Language :: Python :: 3.8
15
14
  Classifier: Programming Language :: Python :: 3.9
16
15
  Classifier: Programming Language :: Python :: 3.10
@@ -26,6 +25,7 @@ Dynamic: description
26
25
  Dynamic: description-content-type
27
26
  Dynamic: keywords
28
27
  Dynamic: license
28
+ Dynamic: license-file
29
29
  Dynamic: summary
30
30
 
31
31
  ===================
@@ -3,17 +3,18 @@ README.rst
3
3
  pyproject.toml
4
4
  setup.py
5
5
  src/DataComparerLibrary/__init__.py
6
- src/DataComparerLibrary/arraycomparer.py
7
6
  src/DataComparerLibrary/datacomparer.py
8
7
  src/DataComparerLibrary/datasorter.py
9
8
  src/DataComparerLibrary/datetimehandler.py
10
9
  src/DataComparerLibrary/delimitertranslator.py
10
+ src/DataComparerLibrary/field.py
11
11
  src/DataComparerLibrary/fileconverter.py
12
12
  src/DataComparerLibrary/report.py
13
+ src/DataComparerLibrary/row.py
13
14
  src/DataComparerLibrary/tools.py
15
+ src/DataComparerLibrary/twodarray.py
14
16
  src/DataComparerLibrary/version.py
15
17
  src/DataComparerLibrary.egg-info/PKG-INFO
16
18
  src/DataComparerLibrary.egg-info/SOURCES.txt
17
19
  src/DataComparerLibrary.egg-info/dependency_links.txt
18
- src/DataComparerLibrary.egg-info/top_level.txt
19
- test/test1.py
20
+ src/DataComparerLibrary.egg-info/top_level.txt
@@ -1,206 +0,0 @@
1
- import fnmatch
2
- import re
3
-
4
- from DataComparerLibrary.datetimehandler import DatetimeHandler
5
- from DataComparerLibrary.report import Report
6
- from DataComparerLibrary.tools import Tools
7
-
8
- class ArrayComparer:
9
- def compare_data(self, actual_data, expected_data_including_templates, template_literals_dict):
10
- difference_found = False
11
- #
12
- if actual_data and type(actual_data[0]) is not list: # only a single row
13
- actual_data = [actual_data,] # add row to tuple of lenght 1
14
- #
15
- if expected_data_including_templates and type(expected_data_including_templates[0]) is not list: # only a single row
16
- expected_data_including_templates = [expected_data_including_templates,] # add row to tuple of lenght 1
17
-
18
- number_of_rows_actual_data = len(actual_data)
19
- number_of_rows_expected_data = len(expected_data_including_templates)
20
-
21
- number_of_rows = max(number_of_rows_actual_data, number_of_rows_expected_data)
22
-
23
- Report.show_2d_array(self, "Actual data", actual_data, 20)
24
- Report.show_2d_array(self, "Expected data", expected_data_including_templates, 20)
25
-
26
- Report().show_header_differences_actual_and_expected_data()
27
-
28
- for row_nr in range(number_of_rows):
29
- if row_nr >= number_of_rows_actual_data:
30
- difference_found = True
31
- if len(expected_data_including_templates[row_nr]) == 0:
32
- Report.show_differences_comparation_result(self, row_nr, 0, "", "", "Row actual data is not PRESENT. Row expected data is EMPTY.")
33
- else:
34
- Report.show_differences_comparation_result(self, row_nr, 0, "", expected_data_including_templates[row_nr][0], "Row actual data is not PRESENT.")
35
- continue
36
- #
37
- if row_nr >= number_of_rows_expected_data:
38
- difference_found = True
39
- if len(actual_data[row_nr]) == 0:
40
- Report.show_differences_comparation_result(self, row_nr, 0, "", "", "Row actual data is EMPTY. Row expected data is not PRESENT.")
41
- else:
42
- Report.show_differences_comparation_result(self, row_nr, 0, actual_data[row_nr][0], "", "Row expected data is not PRESENT.")
43
- continue
44
- #
45
- number_of_columns_actual_data = len(actual_data[row_nr])
46
- number_of_columns_expected_data = len(expected_data_including_templates[row_nr])
47
-
48
- number_of_columns = max(number_of_columns_actual_data, number_of_columns_expected_data)
49
-
50
- for column_nr in range(number_of_columns):
51
- expected_data_including_date_template = None
52
- expected_data_with_wildcard = None
53
- skip_exception_rule_used = False
54
- #
55
- if column_nr >= number_of_columns_actual_data:
56
- difference_found = True
57
- Report.show_differences_comparation_result(self, row_nr, column_nr, "", expected_data_including_templates[row_nr][column_nr], "Column actual data is not PRESENT.")
58
- continue
59
- #
60
- if column_nr >= number_of_columns_expected_data:
61
- difference_found = True
62
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], "", "Column expected data is not PRESENT.")
63
- continue
64
- #
65
- if actual_data[row_nr][column_nr] != expected_data_including_templates[row_nr][column_nr]:
66
- # Replace literal templates with fixed external strings.
67
- if template_literals_dict:
68
- for i in range(0, len(template_literals_dict)):
69
- # key = list(template_literals_dict.keys())[i]
70
- # value = list(template_literals_dict.values())[i]
71
- # print("key: ", key)
72
- # print("value: ", value)
73
- expected_data_including_templates[row_nr][column_nr] = expected_data_including_templates[row_nr][column_nr].replace(list(template_literals_dict.keys())[i], list(template_literals_dict.values())[i])
74
- # print("actual_data[row_nr][column_nr]: \n", actual_data[row_nr][column_nr])
75
- # print("expected_data_including_templates[row_nr][column_nr]: \n", expected_data_including_templates[row_nr][column_nr])
76
-
77
-
78
- # Verify if difference is a matter of string versus integer representation.
79
- if str(actual_data[row_nr][column_nr]) == str(expected_data_including_templates[row_nr][column_nr]):
80
- if isinstance(actual_data[row_nr][column_nr], int) and isinstance(expected_data_including_templates[row_nr][column_nr], str):
81
- difference_found = True
82
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], expected_data_including_templates[row_nr][column_nr], "There is a difference between actual and expected data. Actual data is an integer while expected data is a string.")
83
- elif isinstance(actual_data[row_nr][column_nr], str) and isinstance(expected_data_including_templates[row_nr][column_nr], int):
84
- difference_found = True
85
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], expected_data_including_templates[row_nr][column_nr], "There is a difference between actual and expected data. Actual data is a string while expected data is an integer.")
86
- continue
87
- #
88
- # If data in actual and expected field doesn't match, check if a template has been used in expected data.
89
- match expected_data_including_templates[row_nr][column_nr].upper():
90
- case "{PRESENT}":
91
- if not actual_data[row_nr][column_nr]:
92
- # No data is present in actual data field.
93
- difference_found = True
94
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], expected_data_including_templates[row_nr][column_nr], "Actual data field is not PRESENT")
95
- #
96
- case "{EMPTY}":
97
- if actual_data[row_nr][column_nr]:
98
- # Actual data field is not empty.
99
- difference_found = True
100
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], expected_data_including_templates[row_nr][column_nr], "Actual data field is not EMPTY")
101
- #
102
- case "{INTEGER}":
103
- if isinstance(actual_data[row_nr][column_nr], int):
104
- # A real integer.
105
- continue
106
- #
107
- # Verify if string is integer.
108
- if not actual_data[row_nr][column_nr].isdigit():
109
- # Not positive integer field.
110
- difference_found = True
111
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], expected_data_including_templates[row_nr][column_nr], "Actual data field is not INTEGER.")
112
- #
113
- case "{SKIP}":
114
- pass
115
- case _:
116
- if "{SKIP}" in expected_data_including_templates[row_nr][column_nr].upper() or "{DATETIME_FORMAT():YYYYMMDDHHMMSSFF6}" in expected_data_including_templates[row_nr][column_nr].upper():
117
- if expected_data_including_templates[row_nr][column_nr].upper() == "{SKIP}":
118
- # Complete actual data field will be skipped for verification.
119
- pass
120
- else:
121
- # Part(s) of the actual data field will be skipped for verification.
122
- # Replace {SKIP}, ignoring cases, by wildcard *.
123
- # compiled = re.compile(re.escape("{SKIP}"), re.IGNORECASE)
124
- # expected_data_with_wildcard = compiled.sub("*", expected_data_including_templates[row_nr][column_nr])
125
- compiled = re.compile(re.escape("{SKIP}"), re.IGNORECASE)
126
- compiled2 = re.compile(re.escape("{DATETIME_FORMAT():YYYYMMDDHHMMSSFF6}"), re.IGNORECASE)
127
- expected_data_with_wildcard = compiled2.sub("*", compiled.sub("*", expected_data_including_templates[row_nr][column_nr]))
128
- #
129
- if fnmatch.fnmatch(actual_data[row_nr][column_nr], expected_data_with_wildcard):
130
- skip_exception_rule_used = True
131
- continue
132
- #
133
- if expected_data_with_wildcard is None:
134
- # Wildcards not used.
135
- expected_data_including_date_template = expected_data_including_templates[row_nr][column_nr]
136
- else:
137
- expected_data_including_date_template = expected_data_with_wildcard
138
- #
139
- if "{NOW()" in expected_data_including_templates[row_nr][column_nr].upper():
140
- matches = ["{NOW():", "{NOW()+", "{NOW()-"]
141
- if all([x not in expected_data_including_templates[row_nr][column_nr].upper() for x in matches]):
142
- difference_found = True
143
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], expected_data_including_templates[row_nr][column_nr], "NOW() has been found in expected data field, but format is incorrect.")
144
- continue
145
- #
146
- expected_data = DatetimeHandler.replace_date_template_in_expected_data(self, expected_data_including_date_template)
147
- #
148
- if expected_data == -1:
149
- difference_found = True
150
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], expected_data_including_templates[row_nr][column_nr], "NOW() has been found in expected data field, but format is incorrect.")
151
- else:
152
- if not fnmatch.fnmatch(actual_data[row_nr][column_nr], expected_data):
153
- # No match despite using of wildcard(s).
154
- difference_found = True
155
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], expected_data_including_templates[row_nr][column_nr], "Date template format displayed. See also next message line.")
156
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], expected_data, "There is a difference between actual and expected data.")
157
- continue
158
- #
159
- elif "{NOT(" in expected_data_including_templates[row_nr][column_nr].upper():
160
- try:
161
- unwanted_expected_data = ArrayComparer.__get_unwanted_expected_data(self, expected_data_including_date_template)
162
- #
163
- if actual_data[row_nr][column_nr] == unwanted_expected_data:
164
- # Unwanted match.
165
- difference_found = True
166
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], expected_data_including_templates[row_nr][column_nr], "NOT() template format displayed. See also next message line.")
167
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], unwanted_expected_data, "Actual and expected data are equal. However actual data should NOT be equal to the expected data!!!")
168
- except Exception as exception_message:
169
- # print(f"An exception occurred: {exception_message}")
170
- difference_found = True
171
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], expected_data_including_templates[row_nr][column_nr], "NOT() has been found in expected data field, but format is incorrect.")
172
- #
173
- else:
174
- if not skip_exception_rule_used:
175
- # No exceptions.
176
- difference_found = True
177
- Report.show_differences_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], expected_data_including_templates[row_nr][column_nr], "There is a difference between actual and expected data. No exception rule has been used.")
178
- #
179
- if difference_found:
180
- print("\n\n\n")
181
- raise Exception("There is a difference between actual and expected data. See detail information.")
182
- else:
183
- print("There are no differences between actual and expected data found.")
184
- print("\n\n\n")
185
-
186
- #Report().show_footer_comparation_result()
187
-
188
-
189
- def __get_unwanted_expected_data(self, expected_data_field_including_date_template):
190
- position_open_brace = expected_data_field_including_date_template.find("{NOT(")
191
- position_close_brace = expected_data_field_including_date_template.find(")}", position_open_brace)
192
- #
193
- if position_open_brace == -1:
194
- #print("position_open_brace:", position_open_brace)
195
- raise Exception()
196
- #
197
- if position_close_brace == -1:
198
- #print("position_close_brace:", position_close_brace)
199
- raise Exception()
200
- #
201
- unwanted_expected_data = expected_data_field_including_date_template[position_open_brace+5:position_close_brace]
202
- #
203
- if Tools.is_integer(self, unwanted_expected_data):
204
- unwanted_expected_data = int(unwanted_expected_data)
205
- return unwanted_expected_data
206
-
@@ -1,135 +0,0 @@
1
- # Script for comparing csv-files, 2d-array with a csv-file or 2d-arrays and for comparing text-files, text variable with a text-file or text variables.
2
- #
3
- import csv
4
- import os
5
-
6
- from DataComparerLibrary.arraycomparer import ArrayComparer
7
-
8
-
9
- class DataComparer:
10
- def compare_data_2d_array_with_file(self, actual_data, expected_file, delimiter_expected_data=",", quotechar_expected_data='"', template_literals_dict=None):
11
- self.__check_if_actual_data_is_present(actual_data)
12
- self.__check_if_expected_file_is_present(expected_file)
13
- #
14
- expected_data = self.__open_csv_input_file(expected_file, delimiter_expected_data, quotechar_expected_data)
15
- #
16
- ArrayComparer.compare_data(self, actual_data, expected_data, template_literals_dict)
17
-
18
-
19
- def compare_data_file_with_2d_array(self, actual_file, expected_data, delimiter_actual_data=",", quotechar_actual_data='"', template_literals_dict=None):
20
- self.__check_if_actual_file_is_present(actual_file)
21
- self.__check_if_expected_data_is_present(expected_data)
22
- #
23
- actual_data = self.__open_csv_input_file(actual_file, delimiter_actual_data, quotechar_actual_data)
24
- #
25
- ArrayComparer.compare_data(self, actual_data, expected_data, template_literals_dict)
26
-
27
-
28
- def compare_data_2d_arrays(self, actual_data, expected_data, template_literals_dict=None):
29
- self.__check_if_actual_data_is_present(actual_data)
30
- self.__check_if_expected_data_is_present(expected_data)
31
- #
32
- ArrayComparer.compare_data(self, actual_data, expected_data, template_literals_dict)
33
-
34
-
35
- def compare_data_files(self, actual_file, expected_file, delimiter_actual_data=",", delimiter_expected_data=",", quotechar_actual_data='"', quotechar_expected_data='"', template_literals_dict=None):
36
- self.__check_if_actual_file_is_present(actual_file)
37
- self.__check_if_expected_file_is_present(expected_file)
38
- #
39
- actual_data = self.__open_csv_input_file(actual_file, delimiter_actual_data, quotechar_actual_data)
40
- expected_data = self.__open_csv_input_file(expected_file, delimiter_expected_data, quotechar_expected_data)
41
- #
42
- ArrayComparer.compare_data(self, actual_data, expected_data, template_literals_dict)
43
-
44
-
45
- def compare_text_variable_with_text_file(self, actual_text, expected_file, template_literals_dict=None):
46
- self.__check_if_actual_data_is_present(actual_text)
47
- self.__check_if_expected_file_is_present(expected_file)
48
- #
49
- actual_data = self.__split_text_into_textline_array(actual_text)
50
- expected_data = self.__split_textfile_into_textline_array(expected_file)
51
- #
52
- ArrayComparer.compare_data(self, actual_data, expected_data, template_literals_dict)
53
-
54
-
55
- def compare_text_file_with_text_variable(self, actual_file, expected_text, template_literals_dict=None):
56
- self.__check_if_actual_file_is_present(actual_file)
57
- self.__check_if_expected_data_is_present(expected_text)
58
- #
59
- actual_data = self.__split_textfile_into_textline_array(actual_file)
60
- expected_data = self.__split_text_into_textline_array(expected_text)
61
- #
62
- ArrayComparer.compare_data(self, actual_data, expected_data, template_literals_dict)
63
-
64
-
65
- def compare_text_variables(self, actual_text, expected_text, template_literals_dict=None):
66
- self.__check_if_actual_data_is_present(actual_text)
67
- self.__check_if_expected_data_is_present(expected_text)
68
- #
69
- actual_data = self.__split_text_into_textline_array(actual_text)
70
- expected_data = self.__split_text_into_textline_array(expected_text)
71
- #
72
- ArrayComparer.compare_data(self, actual_data, expected_data, template_literals_dict)
73
-
74
-
75
- def compare_text_files(self, actual_file, expected_file, template_literals_dict=None):
76
- self.__check_if_actual_file_is_present(actual_file)
77
- self.__check_if_expected_file_is_present(expected_file)
78
- #
79
- actual_data = self.__split_textfile_into_textline_array(actual_file)
80
- expected_data = self.__split_textfile_into_textline_array(expected_file)
81
- #
82
- ArrayComparer.compare_data(self, actual_data, expected_data, template_literals_dict)
83
-
84
-
85
-
86
- def __check_if_actual_data_is_present(self, data):
87
- if data == None:
88
- raise Exception("Actual Input data unknown.")
89
-
90
-
91
- def __check_if_expected_data_is_present(self, data):
92
- if data == None:
93
- raise Exception("Expected Input data unknown.")
94
-
95
-
96
- def __check_if_actual_file_is_present(self, file):
97
- if os.path.exists(file):
98
- print("actual_file: ", file)
99
- else:
100
- raise Exception("Actual Input file doesn't exists: ", file)
101
-
102
-
103
- def __check_if_expected_file_is_present(self, file):
104
- if os.path.exists(file):
105
- print("expected_file: ", file)
106
- else:
107
- raise Exception("Expected Input file doesn't exists: ", file)
108
-
109
-
110
- def __open_csv_input_file(self, input_file, delimiter_data=",", quotechar_data='"'):
111
- with open(input_file, mode='rt', encoding='utf-8') as input_file:
112
- if len(delimiter_data) == 1:
113
- data = list(csv.reader(input_file, delimiter=delimiter_data, quotechar=quotechar_data))
114
- else:
115
- data = list(csv.reader((line.replace(delimiter_data, chr(255)) for line in input_file), delimiter=chr(255), quotechar=quotechar_data))
116
- #
117
- return data
118
-
119
-
120
- def __split_text_into_textline_array(self, text):
121
- data = []
122
- for line in text.split('\n'):
123
- data.append(line.strip('\n').split(chr(255)))
124
- #
125
- return data
126
-
127
-
128
- def __split_textfile_into_textline_array(self, input_file):
129
- with open(input_file, mode='rt', encoding='utf-8') as input_file:
130
- data = []
131
- for line in input_file.readlines():
132
- data.append(line.strip('\n').split(chr(255)))
133
- #
134
- return data
135
-
@@ -1,3 +0,0 @@
1
- # encoding=utf-8
2
-
3
- VERSION = '0.845'
@@ -1,21 +0,0 @@
1
- from DataComparerLibrary import DataComparer
2
-
3
- # Input files
4
- actual_file = "C:\\Users\\USER\\Documents\\Tool projecten\\Robot Framework Verzamelmap\\RobotDemo-master-Probeersel\\data_vergelijker\\actueel_resultaat\\actual.csv"
5
- expected_file = "C:\\Users\\USER\\Documents\\Tool projecten\\Robot Framework Verzamelmap\\RobotDemo-master-Probeersel\\data_vergelijker\\verwacht_resultaat\\expected_equal.csv"
6
-
7
-
8
- a = DataComparer()
9
- a.compare_data_files(actual_file, expected_file, delimiter_actual_data=';', delimiter_expected_data=';')
10
- a.compare_data_files(actual_file, expected_file, delimiter_actual_data=';', delimiter_expected_data=';', quotechar_actual_data=None, quotechar_expected_data=None)
11
- a.compare_data_files(actual_file, expected_file)
12
- actual_data_input = [['3', '1.1', '4', '7', '2023-05-27 12:00'],
13
- ['1', '0', '2', '9', '2023-05-29 12:00'],
14
- ['', '2', '0', '1', '5'],
15
- ['5.1', '8', '1', '0', '3'],
16
- ['3', '', '2023-06-19', '3', '2023-06-08']]
17
-
18
- a.compare_data_files(actual_data_input, expected_file)
19
-
20
- a.compare_data_files(actual_file, expected_file)
21
-