DataComparerLibrary 0.844__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 (27) hide show
  1. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/PKG-INFO +11 -3
  2. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/setup.py +0 -1
  3. datacomparerlibrary-0.846/src/DataComparerLibrary/datacomparer.py +162 -0
  4. datacomparerlibrary-0.846/src/DataComparerLibrary/datetimehandler.py +100 -0
  5. datacomparerlibrary-0.846/src/DataComparerLibrary/field.py +166 -0
  6. datacomparerlibrary-0.846/src/DataComparerLibrary/report.py +50 -0
  7. datacomparerlibrary-0.846/src/DataComparerLibrary/row.py +32 -0
  8. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/src/DataComparerLibrary/tools.py +2 -1
  9. datacomparerlibrary-0.846/src/DataComparerLibrary/twodarray.py +36 -0
  10. datacomparerlibrary-0.846/src/DataComparerLibrary/version.py +3 -0
  11. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/src/DataComparerLibrary.egg-info/PKG-INFO +11 -3
  12. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/src/DataComparerLibrary.egg-info/SOURCES.txt +5 -3
  13. datacomparerlibrary-0.844/src/DataComparerLibrary/arraycomparer.py +0 -314
  14. datacomparerlibrary-0.844/src/DataComparerLibrary/datacomparer.py +0 -135
  15. datacomparerlibrary-0.844/src/DataComparerLibrary/report.py +0 -23
  16. datacomparerlibrary-0.844/src/DataComparerLibrary/version.py +0 -3
  17. datacomparerlibrary-0.844/test/test1.py +0 -21
  18. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/LICENSE.txt +0 -0
  19. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/README.rst +0 -0
  20. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/pyproject.toml +0 -0
  21. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/setup.cfg +0 -0
  22. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/src/DataComparerLibrary/__init__.py +0 -0
  23. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/src/DataComparerLibrary/datasorter.py +0 -0
  24. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/src/DataComparerLibrary/delimitertranslator.py +0 -0
  25. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/src/DataComparerLibrary/fileconverter.py +0 -0
  26. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/src/DataComparerLibrary.egg-info/dependency_links.txt +0 -0
  27. {datacomparerlibrary-0.844 → datacomparerlibrary-0.846}/src/DataComparerLibrary.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: DataComparerLibrary
3
- Version: 0.844
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
@@ -19,6 +18,15 @@ Classifier: Programming Language :: Python :: 3.12
19
18
  Classifier: Programming Language :: Python :: 3.13
20
19
  Description-Content-Type: text/x-rst
21
20
  License-File: LICENSE.txt
21
+ Dynamic: author
22
+ Dynamic: author-email
23
+ Dynamic: classifier
24
+ Dynamic: description
25
+ Dynamic: description-content-type
26
+ Dynamic: keywords
27
+ Dynamic: license
28
+ Dynamic: license-file
29
+ Dynamic: summary
22
30
 
23
31
  ===================
24
32
  DataComparerLibrary
@@ -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,100 @@
1
+ import datetime
2
+ import dateutil.relativedelta
3
+ import re
4
+
5
+ class DatetimeHandler:
6
+ def replace_date_template_in_expected_data(self, expected_data_field_including_date_template):
7
+ # Replace date_template in expected data.
8
+ # For example: This is text {NOW()-5Y2M1D:YYYY-MM-DD} and also text. => This is text 2018-05-03 and also text.
9
+ position_open_brace_today_text = expected_data_field_including_date_template.find("{NOW()")
10
+ position_close_brace_today_text = expected_data_field_including_date_template.find("}", position_open_brace_today_text)
11
+ #
12
+ if position_close_brace_today_text == -1:
13
+ return -1
14
+ # Close brace of TODAY has been found.
15
+ #
16
+ expected_datetime_template_string = expected_data_field_including_date_template[position_open_brace_today_text:position_close_brace_today_text + 1]
17
+ expected_datetime_string = DatetimeHandler.__convert_datetime_template_to_datetime(self, expected_datetime_template_string)
18
+ #
19
+ if expected_datetime_string == -1:
20
+ return -1
21
+ # Datetime_template_string has been converted to datetime.
22
+ #
23
+ # Replace expected_datetime_template_string by expected_datetime_string in expected_data_field_including_template.
24
+ compiled = re.compile(re.escape(expected_datetime_template_string), re.IGNORECASE)
25
+ expected_data_with_calculated_date = compiled.sub(expected_datetime_string, expected_data_field_including_date_template)
26
+ #
27
+ return expected_data_with_calculated_date
28
+
29
+
30
+ def __convert_datetime_template_to_datetime(self, expected_datetime_format):
31
+ # Convert expected datetime template into datetime.
32
+ # For example: {NOW():YYYY-MM-DD} => 2023-07-04
33
+ # {NOW():MMDDYY} => 070423
34
+ # {NOW()-5Y3M1D:D-MMMM-YY} => 3-April-18
35
+ # {NOW()-5Y2M1D:YYYY-MMM-DD} => 2018-Apr-03
36
+ # {NOW()-5Y2M1D:YYYYMMDD} => 20180503
37
+ # {NOW()-5Y2M1D:YYYY-M-D} => 2018-5-3
38
+ # {NOW()+2D:DDMMYYYY => 06072023
39
+ # {NOW()-5Y2M1D:YYYY-MM-DD} => 2018-05-03
40
+ template_datetime_string_splitted = expected_datetime_format.split(":")
41
+ #
42
+ match len(template_datetime_string_splitted):
43
+ case 2:
44
+ if template_datetime_string_splitted[0] == "{NOW()":
45
+ # Current date time.
46
+ expected_datetime = datetime.datetime.now()
47
+ else:
48
+ # Adjust date time based on current date time.
49
+ relative_datetime_template_string = template_datetime_string_splitted[0].replace('{NOW()', '')
50
+ relative_datetime = DatetimeHandler.__convert_relative_datetime_template_to_relative_datetime(self, relative_datetime_template_string[1:len(relative_datetime_template_string)])
51
+ if relative_datetime == -1:
52
+ return -1
53
+ else:
54
+ match relative_datetime_template_string[0]:
55
+ case "+":
56
+ expected_datetime = datetime.datetime.now() + relative_datetime
57
+ case "-":
58
+ expected_datetime = datetime.datetime.now() - relative_datetime
59
+ case _:
60
+ return -1
61
+ case _:
62
+ return -1
63
+ #
64
+ year = expected_datetime.strftime("%Y")
65
+ year_2_digits = expected_datetime.strftime("%y")
66
+ month = expected_datetime.strftime("%m")
67
+ month_abbreviated = expected_datetime.strftime("%b")
68
+ month_full = expected_datetime.strftime("%B")
69
+ day = expected_datetime.strftime("%d")
70
+ #
71
+ expected_date = template_datetime_string_splitted[1][0:len(template_datetime_string_splitted[1]) - 1].replace("YYYY", year).replace("YY", year_2_digits).replace("MMMM", month_full).replace("MMM", month_abbreviated).replace("MM", month).replace("M", month.lstrip("0")).replace("DD", day).replace("D", day.lstrip("0"))
72
+ return expected_date
73
+
74
+
75
+ def __convert_relative_datetime_template_to_relative_datetime(self, relative_datetime_str):
76
+ # Convert relative datetime template to relative datetime.
77
+ # For example: 2Y5M1D => 2 years, 5 months, 1 day (used to add to or subtract from current moment / date)
78
+ # \d+ means 1 of more digits; search on character - for example Y;
79
+ regex = re.compile(r'((?P<years>\d+?)Y)?((?P<months>\d+?)M)?((?P<days>\d+?)D)?((?P<hours>\d+?)h)?((?P<minutes>\d+?)m)?((?P<seconds>\d+?)s)?')
80
+ period = regex.match(relative_datetime_str)
81
+
82
+ if not period:
83
+ return -1
84
+
85
+ period = period.groupdict()
86
+ kwargs = {}
87
+ period_names = ["years", "months", "days"]
88
+ #
89
+ for name, param in period.items():
90
+ if param:
91
+ period_name = name
92
+ period_count = param
93
+ #
94
+ if period_name in period_names:
95
+ kwargs[period_name] = int(period_count)
96
+ #
97
+ if kwargs:
98
+ return dateutil.relativedelta.relativedelta(**kwargs)
99
+ else:
100
+ return -1
@@ -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
+
@@ -0,0 +1,50 @@
1
+ class Report:
2
+ @staticmethod
3
+ def show_header_and_data(header, data):
4
+ Report.show_2d_array(header, data, 20)
5
+
6
+
7
+ @staticmethod
8
+ def show_header_differences_actual_and_expected_data():
9
+ print()
10
+ print("=== Overview differences between actual and expected data")
11
+ print()
12
+
13
+
14
+ #show_difference_between_actual_and_expected_data
15
+ @staticmethod
16
+ def show_differences_comparation_result(row_number, column_number, actual_data, expected_data, error_message):
17
+ print("Row: ", row_number + 1, " Column: ", column_number + 1, " => Actual data: ", actual_data, " Expected data: ", expected_data, " Remark / Error message: ", error_message)
18
+
19
+
20
+ @staticmethod
21
+ def show_footer_comparation_result(equal):
22
+ if equal:
23
+ print("\n\n\n")
24
+ print("There are no differences between actual and expected data found.")
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
+
30
+
31
+
32
+ @staticmethod
33
+ def show_2d_array(title, reader_file_list, column_width):
34
+ max_length_title = 30
35
+ title = title[0:(max_length_title - 1)]
36
+ length_title = len(title)
37
+ print("=== ", title, " ", end="")
38
+ print("=" * (max_length_title - length_title))
39
+ print()
40
+ #
41
+ for row in reader_file_list:
42
+ for cell_value in row:
43
+ #if isinstance(cell_value, str):
44
+ if isinstance(cell_value, str) or isinstance(cell_value, int):
45
+ #print('{val:{fill}{width}}'.format(val=cell_value, fill='', width=column_width), end=" ")
46
+ print('{val:{fill}{width}}'.format(val=cell_value, fill='', width=column_width, left_aligned=True), end=" ")
47
+
48
+ print()
49
+ print()
50
+ print()
@@ -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.1
1
+ Metadata-Version: 2.4
2
2
  Name: DataComparerLibrary
3
- Version: 0.844
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
@@ -19,6 +18,15 @@ Classifier: Programming Language :: Python :: 3.12
19
18
  Classifier: Programming Language :: Python :: 3.13
20
19
  Description-Content-Type: text/x-rst
21
20
  License-File: LICENSE.txt
21
+ Dynamic: author
22
+ Dynamic: author-email
23
+ Dynamic: classifier
24
+ Dynamic: description
25
+ Dynamic: description-content-type
26
+ Dynamic: keywords
27
+ Dynamic: license
28
+ Dynamic: license-file
29
+ Dynamic: summary
22
30
 
23
31
  ===================
24
32
  DataComparerLibrary
@@ -3,16 +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
8
+ src/DataComparerLibrary/datetimehandler.py
9
9
  src/DataComparerLibrary/delimitertranslator.py
10
+ src/DataComparerLibrary/field.py
10
11
  src/DataComparerLibrary/fileconverter.py
11
12
  src/DataComparerLibrary/report.py
13
+ src/DataComparerLibrary/row.py
12
14
  src/DataComparerLibrary/tools.py
15
+ src/DataComparerLibrary/twodarray.py
13
16
  src/DataComparerLibrary/version.py
14
17
  src/DataComparerLibrary.egg-info/PKG-INFO
15
18
  src/DataComparerLibrary.egg-info/SOURCES.txt
16
19
  src/DataComparerLibrary.egg-info/dependency_links.txt
17
- src/DataComparerLibrary.egg-info/top_level.txt
18
- test/test1.py
20
+ src/DataComparerLibrary.egg-info/top_level.txt
@@ -1,314 +0,0 @@
1
- import datetime
2
- import fnmatch
3
- import re
4
- import dateutil.relativedelta
5
-
6
- from DataComparerLibrary.report import Report
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 = number_of_rows_actual_data if (number_of_rows_actual_data >= number_of_rows_expected_data) else 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
- print()
27
- print("=== Overview differences between actual and expected data")
28
- print()
29
-
30
- for row_nr in range(number_of_rows):
31
- if row_nr >= number_of_rows_actual_data:
32
- difference_found = True
33
- if len(expected_data_including_templates[row_nr]) == 0:
34
- Report.show_comparation_result(self, row_nr, 0, "", "", "Row actual data is not PRESENT. Row expected data is EMPTY.")
35
- else:
36
- Report.show_comparation_result(self, row_nr, 0, "", expected_data_including_templates[row_nr][0], "Row actual data is not PRESENT.")
37
- continue
38
- #
39
- if row_nr >= number_of_rows_expected_data:
40
- difference_found = True
41
- if len(actual_data[row_nr]) == 0:
42
- Report.show_comparation_result(self, row_nr, 0, "", "", "Row actual data is EMPTY. Row expected data is not PRESENT.")
43
- else:
44
- Report.show_comparation_result(self, row_nr, 0, actual_data[row_nr][0], "", "Row expected data is not PRESENT.")
45
- continue
46
- #
47
- number_of_columns_actual_data = len(actual_data[row_nr])
48
- number_of_columns_expected_data = len(expected_data_including_templates[row_nr])
49
-
50
- number_of_columns = number_of_columns_actual_data if (number_of_columns_actual_data >= number_of_columns_expected_data) else number_of_columns_expected_data
51
-
52
- for column_nr in range(number_of_columns):
53
- expected_data_including_date_template = None
54
- expected_data_with_wildcard = None
55
- skip_exception_rule_used = False
56
- #
57
- if column_nr >= number_of_columns_actual_data:
58
- difference_found = True
59
- Report.show_comparation_result(self, row_nr, column_nr, "", expected_data_including_templates[row_nr][column_nr], "Column actual data is not PRESENT.")
60
- continue
61
- #
62
- if column_nr >= number_of_columns_expected_data:
63
- difference_found = True
64
- Report.show_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], "", "Column expected data is not PRESENT.")
65
- continue
66
- #
67
- if actual_data[row_nr][column_nr] != expected_data_including_templates[row_nr][column_nr]:
68
- # Replace literal templates with fixed external strings.
69
- if template_literals_dict:
70
- for i in range(0, len(template_literals_dict)):
71
- # key = list(template_literals_dict.keys())[i]
72
- # value = list(template_literals_dict.values())[i]
73
- # print("key: ", key)
74
- # print("value: ", value)
75
- 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])
76
- # print("actual_data[row_nr][column_nr]: \n", actual_data[row_nr][column_nr])
77
- # print("expected_data_including_templates[row_nr][column_nr]: \n", expected_data_including_templates[row_nr][column_nr])
78
-
79
-
80
- # Verify if difference is a matter of string versus integer representation.
81
- if str(actual_data[row_nr][column_nr]) == str(expected_data_including_templates[row_nr][column_nr]):
82
- if isinstance(actual_data[row_nr][column_nr], int) and isinstance(expected_data_including_templates[row_nr][column_nr], str):
83
- difference_found = True
84
- Report.show_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.")
85
- elif isinstance(actual_data[row_nr][column_nr], str) and isinstance(expected_data_including_templates[row_nr][column_nr], int):
86
- difference_found = True
87
- Report.show_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.")
88
- continue
89
- #
90
- # If data in actual and expected field doesn't match, check if a template has been used in expected data.
91
- match expected_data_including_templates[row_nr][column_nr].upper():
92
- case "{PRESENT}":
93
- if not actual_data[row_nr][column_nr]:
94
- # No data is present in actual data field.
95
- difference_found = True
96
- Report.show_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")
97
- #
98
- case "{EMPTY}":
99
- if actual_data[row_nr][column_nr]:
100
- # Actual data field is not empty.
101
- difference_found = True
102
- Report.show_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")
103
- #
104
- case "{INTEGER}":
105
- if isinstance(actual_data[row_nr][column_nr], int):
106
- # A real integer.
107
- continue
108
- #
109
- # Verify if string is integer.
110
- if not actual_data[row_nr][column_nr].isdigit():
111
- # Not positive integer field.
112
- difference_found = True
113
- Report.show_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.")
114
- #
115
- case "{SKIP}":
116
- pass
117
- case _:
118
- 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():
119
- if expected_data_including_templates[row_nr][column_nr].upper() == "{SKIP}":
120
- # Complete actual data field will be skipped for verification.
121
- pass
122
- else:
123
- # Part(s) of the actual data field will be skipped for verification.
124
- # Replace {SKIP}, ignoring cases, by wildcard *.
125
- # compiled = re.compile(re.escape("{SKIP}"), re.IGNORECASE)
126
- # expected_data_with_wildcard = compiled.sub("*", expected_data_including_templates[row_nr][column_nr])
127
- compiled = re.compile(re.escape("{SKIP}"), re.IGNORECASE)
128
- compiled2 = re.compile(re.escape("{DATETIME_FORMAT():YYYYMMDDHHMMSSFF6}"), re.IGNORECASE)
129
- expected_data_with_wildcard = compiled2.sub("*", compiled.sub("*", expected_data_including_templates[row_nr][column_nr]))
130
- #
131
- if fnmatch.fnmatch(actual_data[row_nr][column_nr], expected_data_with_wildcard):
132
- skip_exception_rule_used = True
133
- continue
134
- #
135
- if expected_data_with_wildcard is None:
136
- # Wildcards not used.
137
- expected_data_including_date_template = expected_data_including_templates[row_nr][column_nr]
138
- else:
139
- expected_data_including_date_template = expected_data_with_wildcard
140
- #
141
- if "{NOW()" in expected_data_including_templates[row_nr][column_nr].upper():
142
- matches = ["{NOW():", "{NOW()+", "{NOW()-"]
143
- if all([x not in expected_data_including_templates[row_nr][column_nr].upper() for x in matches]):
144
- difference_found = True
145
- Report.show_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.")
146
- continue
147
- #
148
- expected_data = ArrayComparer.__replace_date_template_in_expected_data(self, expected_data_including_date_template)
149
- #
150
- if expected_data == -1:
151
- difference_found = True
152
- Report.show_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.")
153
- else:
154
- if not fnmatch.fnmatch(actual_data[row_nr][column_nr], expected_data):
155
- # No match despite using of wildcard(s).
156
- difference_found = True
157
- Report.show_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.")
158
- Report.show_comparation_result(self, row_nr, column_nr, actual_data[row_nr][column_nr], expected_data, "There is a difference between actual and expected data.")
159
- continue
160
- #
161
- elif "{NOT(" in expected_data_including_templates[row_nr][column_nr].upper():
162
- try:
163
- unwanted_expected_data = ArrayComparer.__get_unwanted_expected_data(self, expected_data_including_date_template)
164
- #
165
- if actual_data[row_nr][column_nr] == unwanted_expected_data:
166
- # Unwanted match.
167
- difference_found = True
168
- Report.show_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.")
169
- Report.show_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!!!")
170
- except Exception as exception_message:
171
- # print(f"An exception occurred: {exception_message}")
172
- difference_found = True
173
- Report.show_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.")
174
- #
175
- else:
176
- if not skip_exception_rule_used:
177
- # No exceptions.
178
- difference_found = True
179
- Report.show_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.")
180
- #
181
- if difference_found:
182
- print("\n\n\n")
183
- raise Exception("There is a difference between actual and expected data. See detail information.")
184
- else:
185
- print("There are no differences between actual and expected data found.")
186
- print("\n\n\n")
187
-
188
-
189
- def __replace_date_template_in_expected_data(self, expected_data_field_including_date_template):
190
- # Replace date_template in expected data.
191
- # For example: This is text {NOW()-5Y2M1D:YYYY-MM-DD} and also text. => This is text 2018-05-03 and also text.
192
- position_open_brace_today_text = expected_data_field_including_date_template.find("{NOW()")
193
- position_close_brace_today_text = expected_data_field_including_date_template.find("}", position_open_brace_today_text)
194
- #
195
- if position_close_brace_today_text == -1:
196
- return -1
197
- # Close brace of TODAY has been found.
198
- #
199
- expected_datetime_template_string = expected_data_field_including_date_template[position_open_brace_today_text:position_close_brace_today_text + 1]
200
- expected_datetime_string = ArrayComparer.__convert_datetime_template_to_datetime(self, expected_datetime_template_string)
201
- #
202
- if expected_datetime_string == -1:
203
- return -1
204
- # Datetime_template_string has been converted to datetime.
205
- #
206
- # Replace expected_datetime_template_string by expected_datetime_string in expected_data_field_including_template.
207
- compiled = re.compile(re.escape(expected_datetime_template_string), re.IGNORECASE)
208
- expected_data_with_calculated_date = compiled.sub(expected_datetime_string, expected_data_field_including_date_template)
209
- #
210
- return expected_data_with_calculated_date
211
-
212
-
213
- def __convert_datetime_template_to_datetime(self, expected_datetime_format):
214
- # Convert expected datetime template into datetime.
215
- # For example: {NOW():YYYY-MM-DD} => 2023-07-04
216
- # {NOW():MMDDYY} => 070423
217
- # {NOW()-5Y3M1D:D-MMMM-YY} => 3-April-18
218
- # {NOW()-5Y2M1D:YYYY-MMM-DD} => 2018-Apr-03
219
- # {NOW()-5Y2M1D:YYYYMMDD} => 20180503
220
- # {NOW()-5Y2M1D:YYYY-M-D} => 2018-5-3
221
- # {NOW()+2D:DDMMYYYY => 06072023
222
- # {NOW()-5Y2M1D:YYYY-MM-DD} => 2018-05-03
223
- template_datetime_string_splitted = expected_datetime_format.split(":")
224
- #
225
- match len(template_datetime_string_splitted):
226
- case 2:
227
- if template_datetime_string_splitted[0] == "{NOW()":
228
- # Current date time.
229
- expected_datetime = datetime.datetime.now()
230
- else:
231
- # Adjust date time based on current date time.
232
- relative_datetime_template_string = template_datetime_string_splitted[0].replace('{NOW()', '')
233
- relative_datetime = ArrayComparer.__convert_relative_datetime_template_to_relative_datetime(self, relative_datetime_template_string[1:len(relative_datetime_template_string)])
234
- if relative_datetime == -1:
235
- return -1
236
- else:
237
- match relative_datetime_template_string[0]:
238
- case "+":
239
- expected_datetime = datetime.datetime.now() + relative_datetime
240
- case "-":
241
- expected_datetime = datetime.datetime.now() - relative_datetime
242
- case _:
243
- return -1
244
- case _:
245
- return -1
246
- #
247
- year = expected_datetime.strftime("%Y")
248
- year_2_digits = expected_datetime.strftime("%y")
249
- month = expected_datetime.strftime("%m")
250
- month_abbreviated = expected_datetime.strftime("%b")
251
- month_full = expected_datetime.strftime("%B")
252
- day = expected_datetime.strftime("%d")
253
- #
254
- expected_date = template_datetime_string_splitted[1][0:len(template_datetime_string_splitted[1]) - 1].replace("YYYY", year).replace("YY", year_2_digits).replace("MMMM", month_full).replace("MMM", month_abbreviated).replace("MM", month).replace("M", month.lstrip("0")).replace("DD", day).replace("D", day.lstrip("0"))
255
- return expected_date
256
-
257
-
258
- def __convert_relative_datetime_template_to_relative_datetime(self, relative_datetime_str):
259
- # Convert relative datetime template to relative datetime.
260
- # For example: 2Y5M1D => 2 years, 5 months, 1 day (used to add to or subtract from current moment / date)
261
- # \d+ means 1 of more digits; search on character - for example Y;
262
- regex = re.compile(r'((?P<years>\d+?)Y)?((?P<months>\d+?)M)?((?P<days>\d+?)D)?((?P<hours>\d+?)h)?((?P<minutes>\d+?)m)?((?P<seconds>\d+?)s)?')
263
- period = regex.match(relative_datetime_str)
264
-
265
- if not period:
266
- return -1
267
-
268
- period = period.groupdict()
269
- kwargs = {}
270
- period_names = ["years", "months", "days"]
271
- #
272
- for name, param in period.items():
273
- if param:
274
- period_name = name
275
- period_count = param
276
- #
277
- if period_name in period_names:
278
- kwargs[period_name] = int(period_count)
279
- #
280
- if kwargs:
281
- return dateutil.relativedelta.relativedelta(**kwargs)
282
- else:
283
- return -1
284
-
285
-
286
-
287
- def __get_unwanted_expected_data(self, expected_data_field_including_date_template):
288
- position_open_brace = expected_data_field_including_date_template.find("{NOT(")
289
- position_close_brace = expected_data_field_including_date_template.find(")}", position_open_brace)
290
- #
291
- if position_open_brace == -1:
292
- #print("position_open_brace:", position_open_brace)
293
- raise Exception()
294
- #
295
- if position_close_brace == -1:
296
- #print("position_close_brace:", position_close_brace)
297
- raise Exception()
298
- #
299
- unwanted_expected_data = expected_data_field_including_date_template[position_open_brace+5:position_close_brace]
300
- #
301
- if ArrayComparer.is_integer(self, unwanted_expected_data):
302
- unwanted_expected_data = int(unwanted_expected_data)
303
- return unwanted_expected_data
304
-
305
-
306
-
307
- def is_integer(self, string):
308
- if string[0] == '-':
309
- # if a negative number
310
- return string[1:].isdigit()
311
- else:
312
- return string.isdigit()
313
-
314
-
@@ -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,23 +0,0 @@
1
- class Report:
2
- def show_2d_array(self, title, reader_file_list, column_width):
3
- max_length_title = 30
4
- title = title[0:(max_length_title - 1)]
5
- length_title = len(title)
6
- print("=== ", title, " ", end="")
7
- print("=" * (max_length_title - length_title))
8
- print()
9
- #
10
- for row in reader_file_list:
11
- for cell_value in row:
12
- #if isinstance(cell_value, str):
13
- if isinstance(cell_value, str) or isinstance(cell_value, int):
14
- #print('{val:{fill}{width}}'.format(val=cell_value, fill='', width=column_width), end=" ")
15
- print('{val:{fill}{width}}'.format(val=cell_value, fill='', width=column_width, left_aligned=True), end=" ")
16
-
17
- print()
18
- print()
19
- print()
20
-
21
-
22
- def show_comparation_result(self, row_number, column_number, actual_data, expected_data, error_message):
23
- print("Row: ", row_number + 1, " Column: ", column_number + 1, " => Actual data: ", actual_data, " Expected data: ", expected_data, " Remark / Error message: ", error_message)
@@ -1,3 +0,0 @@
1
- # encoding=utf-8
2
-
3
- VERSION = '0.844'
@@ -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
-