Skip to content

Commit 3daa100

Browse files
authored
Added utility to crawl + compare runs (#1)
2 parents d73c974 + dec68ab commit 3daa100

1 file changed

Lines changed: 161 additions & 0 deletions

File tree

spot_the_difference.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#!/usr/bin/env python
2+
3+
4+
from argparse import ArgumentParser
5+
6+
import os.path as op
7+
import numpy as np
8+
import hashlib
9+
import json
10+
import os
11+
import re
12+
13+
14+
def get_seq_steplist(dirs):
15+
if not isinstance(dirs, list):
16+
dirs = [dirs]
17+
18+
r = re.compile('.+_[0-9]+$')
19+
fl_lists = []
20+
for di in dirs:
21+
fls = sorted(os.listdir(di))
22+
fls_filtered = [f for f in fls if r.match(f)]
23+
fl_inds = [int(_.split('_')[-1]) for _ in fls_filtered]
24+
25+
fls_sorted = [fls_filtered[_] for _ in np.argsort(fl_inds)]
26+
27+
fl_lists += [fls_sorted]
28+
return fl_lists
29+
30+
31+
def prune_and_match(file_lists):
32+
ref = file_lists[0]
33+
new_flist = [ref]
34+
for flist in file_lists[1:]:
35+
new_flist += [ [item for item in flist if item in ref] ]
36+
return new_flist
37+
38+
39+
def find_outputs(dir_path):
40+
try:
41+
with open(op.join(dir_path, "_report/report.rst")) as fhandle:
42+
summary = fhandle.read()
43+
except FileNotFoundError:
44+
return {}
45+
46+
summary = summary.split('\n\n\n')
47+
outp_loc = [1 + idx
48+
for idx, row in enumerate(summary)
49+
if 'Execution Outputs' in row][0]
50+
outputs = summary[outp_loc].split('\n')
51+
52+
kys = [op.split()[1] for op in outputs]
53+
vls = [" ".join(op.split()[3:]) for op in outputs]
54+
55+
fls = {}
56+
for k, v in zip(kys, vls):
57+
if v == '<undefined>':
58+
continue
59+
if v.startswith('['):
60+
v = json.loads(v.replace("'", '"'))
61+
else:
62+
v = [v]
63+
64+
v = [op.basename(_) for _ in v]
65+
# Build the dictionary in reverse to easily look up output names
66+
for _ in v:
67+
fls[_] = k
68+
69+
return fls
70+
71+
72+
def md5_compare(comparable_steps, basepath, path_mods):
73+
ref_list, ref_mod = comparable_steps[0], path_mods[0]
74+
hash_dict = {}
75+
for idx, fdirs in enumerate(comparable_steps):
76+
if idx == 0:
77+
dk = 'ref'
78+
setting = "Reference"
79+
else:
80+
dk = 'test' + str(idx)
81+
setting = "Test " + str(idx)
82+
83+
hash_dict[dk] = {}
84+
print('Computing hashes for {0}...'.format(setting))
85+
86+
for fdir in fdirs:
87+
fpath = op.join(basepath, path_mods[idx], fdir)
88+
outputs = find_outputs(fpath)
89+
90+
hash_dict[dk][fdir] = {}
91+
for key in outputs.keys():
92+
tmp_fl = op.join(fpath, key)
93+
if op.exists(tmp_fl):
94+
hash_dict[dk][fdir][outputs[key]] = md5(tmp_fl)
95+
96+
return hash_dict
97+
98+
99+
def md5(fl):
100+
# From:
101+
# https://stackoverflow.com/questions/22058048/hashing-a-file-in-python
102+
# BUF_SIZE is totally arbitrary, change for your app!
103+
BUF_SIZE = 65536 # lets read stuff in 64kb chunks!
104+
md5 = hashlib.md5()
105+
106+
with open(fl, 'rb') as f:
107+
while True:
108+
data = f.read(BUF_SIZE)
109+
if not data:
110+
break
111+
md5.update(data)
112+
113+
return md5.hexdigest()
114+
115+
116+
def flag_differences(file_hashes):
117+
tests = set(file_hashes.keys()) - set(['ref'])
118+
print("Comparing hashes...")
119+
for key in file_hashes['ref'].keys():
120+
rdat = file_hashes['ref'][key]
121+
print(" Comparing {0}...".format(key))
122+
for t in tests:
123+
tdat = file_hashes[t][key]
124+
print(" {0}:".format(t.capitalize()), end='\t')
125+
126+
diff = []
127+
for k in rdat.keys():
128+
if rdat[k] == {}:
129+
diff += ["no data"]
130+
elif rdat[k] == tdat[k]:
131+
continue
132+
else:
133+
diff += [k]
134+
if len(diff) == 0:
135+
diff = "SUCCESS!"
136+
else:
137+
diff = "FAILED for " + ", ".join(diff)
138+
139+
print(diff)
140+
141+
142+
def main():
143+
parser = ArgumentParser()
144+
parser.add_argument("ref", help="Working directory for the reference.")
145+
parser.add_argument("test", help="Working directory for the test-setting.")
146+
147+
res = parser.parse_args()
148+
149+
step_lists = get_seq_steplist([res.ref, res.test])
150+
comparable_steps = prune_and_match(step_lists)
151+
152+
bp = op.commonpath([res.ref, res.test])
153+
pdiffs = [op.relpath(p, bp) for p in [res.ref, res.test]]
154+
155+
file_hashes = md5_compare(comparable_steps, bp, pdiffs)
156+
157+
flag_differences(file_hashes)
158+
159+
160+
if __name__ == "__main__":
161+
main()

0 commit comments

Comments
 (0)