Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inverse Reporting - Shows whats not changed #15

Merged
merged 3 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 0 additions & 17 deletions 6_14.yaml

This file was deleted.

10 changes: 10 additions & 0 deletions 6_14_constants.yaml.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
expected_constants:
# These are just examples, please replace these with some real expected constants
hosts:
content_facet_something: content_source_something_id

skipped_constants:
# These are just examples, please replace these with some real skip-able constants
content_view_versions:
environments:
permissions_something: something here
10 changes: 10 additions & 0 deletions 6_14_variations.yaml.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
expected_variations:
# These are just examples, please replace these with some real expected variations
hosts:
content_facet_somevar: content_source_ssomevar_id

skipped_variations:
# These are just examples, please replace these with some real skip-able variations
content_view_versions:
environments:
permissions_somevar: somevar_here
5 changes: 3 additions & 2 deletions candore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,17 @@ def compare_entities(
self,
pre_file=None,
post_file=None,
inverse=None,
output=None,
report_type=None,
record_evs=None,
):
comp = Comparator(settings=self.settings)
if record_evs:
comp.record_evs = True
results = comp.compare_json(pre_file=pre_file, post_file=post_file)
results = comp.compare_json(pre_file=pre_file, post_file=post_file, inverse=inverse)
reporter = Reporting(results=results)
reporter.generate_report(output_file=output, output_type=report_type)
reporter.generate_report(output_file=output, output_type=report_type, inverse=inverse)

def find_path(self, path, json_file, delimiter):
finder = Finder()
Expand Down
4 changes: 3 additions & 1 deletion candore/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def extract(ctx, mode, output, full):
@candore.command(help="Compare pre and post upgrade data")
@click.option("--pre", type=str, help="The pre upgrade json file")
@click.option("--post", type=str, help="The post upgrade json file")
@click.option("-i", "--inverse", is_flag=True, help="Inverse comparison, shows whats not changed")
@click.option("-o", "--output", type=str, help="The output file name")
@click.option(
"-t",
Expand All @@ -64,11 +65,12 @@ def extract(ctx, mode, output, full):
)
@click.option("--record-evs", is_flag=True, help="Record Expected Variations in reporting")
@click.pass_context
def compare(ctx, pre, post, output, report_type, record_evs):
def compare(ctx, pre, post, inverse, output, report_type, record_evs):
candore_obj = ctx.parent.candore
candore_obj.compare_entities(
pre_file=pre,
post_file=post,
inverse=inverse,
output=output,
report_type=report_type,
record_evs=record_evs,
Expand Down
97 changes: 68 additions & 29 deletions candore/modules/comparator.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import json

from candore.modules.variatons import Variations
from candore.utils import last_index_of_element
from candore.modules.variations import Variations, Constants
from candore.utils import last_index_of_element, is_list_contains_dict


class Comparator:
def __init__(self, settings):
self.big_key = []
self.big_compare = {}
self.big_diff = {}
self.big_constant = {}
self.record_evs = False
self.variations = Variations(settings)
self.constants = Constants(settings)
self.expected_variations = self.variations.expected_variations
self.skipped_variations = self.variations.skipped_variations
self.expected_constants = self.constants.expected_constants
self.skipped_constants = self.constants.skipped_constants

def remove_non_variant_key(self, key):


def remove_verifed_key(self, key):
reversed_bk = self.big_key[::-1]
if key in reversed_bk:
reversed_bk.remove(key)
Expand All @@ -36,13 +42,32 @@ def record_variation(self, pre, post, var_details=None):
"post": post,
"variation": var_details or "Expected(A)",
}
self.big_compare.update({full_path: variation})
self.big_diff.update({full_path: variation})
elif (
var_full_path not in self.expected_variations
and var_full_path not in self.skipped_variations
):
variation = {"pre": pre, "post": post, "variation": var_details or ""}
self.big_compare.update({full_path: variation})
self.big_diff.update({full_path: variation})

def record_constants(self, pre, post, var_details=None):
big_key = [str(itm) for itm in self.big_key]
full_path = "/".join(big_key)
var_full_path = "/".join([itm for itm in self.big_key if not isinstance(itm, int)])
if var_full_path in self.expected_constants or var_full_path in self.skipped_constants:
if self.record_evs:
variation = {
"pre": pre,
"post": post,
"constant": var_details or "Expected(A)",
}
self.big_constant.update({full_path: variation})
elif (
var_full_path not in self.expected_constants
and var_full_path not in self.skipped_constants
):
variation = {"pre": pre, "post": post, "constant": var_details or ""}
self.big_constant.update({full_path: variation})

def _is_data_type_dict(self, pre, post, unique_key=""):
if (pre and 'id' in pre) and (post and 'id' in post):
Expand All @@ -62,32 +87,41 @@ def _is_data_type_dict(self, pre, post, unique_key=""):
)
self.remove_path(unique_key)

def _is_data_type_list(self, pre, post, unique_key=""):
def _is_data_type_list_contains_dict(self, pre, post):
for pre_entity in pre:
if not pre_entity:
continue
if type(pre_entity) is dict:
for post_entity in post:
if not post_entity:
continue
if "id" in pre_entity:
if pre_entity["id"] == post_entity["id"]:
self.compare_all_pres_with_posts(
pre_entity, post_entity, unique_key=pre_entity["id"]
)
else:
key = list(pre_entity.keys())[0]
if pre_entity[key] == post_entity[key]:
self.compare_all_pres_with_posts(
pre_entity[key], post_entity[key], unique_key=key
)
for post_entity in post:
if not post_entity:
continue
if "id" in pre_entity:
self.remove_path(pre_entity["id"])
if pre_entity["id"] == post_entity["id"]:
self.compare_all_pres_with_posts(
pre_entity, post_entity, unique_key=pre_entity["id"]
)
else:
self.remove_path(pre_entity[list(pre_entity.keys())[0]])
key = list(pre_entity.keys())[0]
if pre_entity[key] == post_entity[key]:
self.compare_all_pres_with_posts(
pre_entity[key], post_entity[key], unique_key=key
)
if "id" in pre_entity:
self.remove_path(pre_entity["id"])
else:
if pre_entity not in post:
self.record_variation(pre, post)
self.remove_path(pre_entity[list(pre_entity.keys())[0]])

def _is_data_type_list(self, pre, post, unique_key=""):

def custom_key(elem):
return 'None' if elem is None else str(elem)

if not is_list_contains_dict(pre):
if sorted(pre, key=custom_key) != sorted(post, key=custom_key):
self.record_variation(pre, post)
else:
self.record_constants(pre, post)
else:
self._is_data_type_list_contains_dict(pre, post)
self.remove_path(unique_key)

def compare_all_pres_with_posts(self, pre_data, post_data, unique_key="", var_details=None):
Expand All @@ -100,9 +134,11 @@ def compare_all_pres_with_posts(self, pre_data, post_data, unique_key="", var_de
else:
if pre_data != post_data:
self.record_variation(pre_data, post_data, var_details)
self.remove_non_variant_key(unique_key)
else:
self.record_constants(pre_data, post_data, var_details)
self.remove_verifed_key(unique_key)

def compare_json(self, pre_file, post_file):
def compare_json(self, pre_file, post_file, inverse):
pre_data = post_data = None

with open(pre_file, "r") as fpre:
Expand All @@ -112,4 +148,7 @@ def compare_json(self, pre_file, post_file):
post_data = json.load(fpost)

self.compare_all_pres_with_posts(pre_data, post_data)
return self.big_compare
if not inverse:
return self.big_diff
else:
return self.big_constant
18 changes: 12 additions & 6 deletions candore/modules/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ def __init__(self, results):
"""
self.results = results

def generate_report(self, output_file, output_type):
def generate_report(self, output_file, output_type, inverse):
"""Generate a report of the compared results

Args:
output_file (str): The file to write the report to
output_type (str): The type of report to generate json / CSV
inverse (bool): Shows what not changed in comparison results
Returns:
None
Raises:
Expand All @@ -31,9 +32,9 @@ def generate_report(self, output_file, output_type):
if output_type == "json":
self._generate_json_report(output_file)
elif output_type == "html":
self._generate_html_report()
print('The HTML reporting is not implemented yet! Try next time!')
elif output_type == "csv":
self._generate_csv_report(output_file)
self._generate_csv_report(output_file, inverse=inverse)
else:
raise ValueError("Output type {} not supported".format(output_type))

Expand Down Expand Up @@ -65,7 +66,7 @@ def _generate_html_report(self):
# render_webpage()
print("HTML report is ready to view at: http://localhost:5000")

def _generate_csv_report(self, output_file):
def _generate_csv_report(self, output_file, inverse):
"""Generate a CSV report of the compared results

Args:
Expand All @@ -78,8 +79,13 @@ def _generate_csv_report(self, output_file):
output_file = Path(output_file)
# Convert json to csv and write to output file
csv_writer = csv.writer(output_file.open("w"))
csv_writer.writerow(["Variation Path", "Pre-Upgrade", "Post-Upgrade", "Variation"])
# Table Column Names
columns = ["Path", "Pre-Upgrade", "Post-Upgrade", "Variation?" if not inverse else 'Constant?']
csv_writer.writerow(columns)
# Writing Rows
for var_path, vals in self.results.items():
csv_writer.writerow([var_path, vals["pre"], vals["post"], vals["variation"]])
csv_writer.writerow([
var_path, vals["pre"], vals["post"],
vals["variation" if not inverse else "constant"]])
print("Wrote CSV report to {}".format(output_file))
print("CSV report contains {} results".format(len(self.results)))
44 changes: 44 additions & 0 deletions candore/modules/variations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""
A module responsible for calculating expected and skipped variations from
`conf/variations` yaml file and convert them into processable list
"""
from functools import cached_property
from candore.utils import yaml_reader, get_yaml_paths

import yaml


class Variations:
def __init__(self, settings):
self.settings = settings

@cached_property
def variations(self):
yaml_data = yaml_reader(file_path=self.settings.candore.var_file)
return yaml_data

@cached_property
def expected_variations(self):
return get_yaml_paths(yaml_data=self.variations.get("expected_variations"))

@cached_property
def skipped_variations(self):
return get_yaml_paths(yaml_data=self.variations.get("skipped_variations"))


class Constants:
def __init__(self, settings):
self.settings = settings

@cached_property
def constants(self):
yaml_data = yaml_reader(file_path=self.settings.candore.constant_file)
return yaml_data

@cached_property
def expected_constants(self):
return get_yaml_paths(yaml_data=self.constants.get("expected_constants"))

@cached_property
def skipped_constants(self):
return get_yaml_paths(yaml_data=self.constants.get("skipped_constants"))
43 changes: 0 additions & 43 deletions candore/modules/variatons.py

This file was deleted.

Loading