Skip to content

Commit 9044399

Browse files
authored
Merge pull request #66 from nexB/59-flatten-deltacode-deltas-field
Flatten DeltaCode.deltas field #59
2 parents 8de457a + c32250c commit 9044399

19 files changed

Lines changed: 280 additions & 568 deletions

src/deltacode/__init__.py

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,15 @@ def __init__(self, new_path, old_path, options):
5050
self.new = Scan(new_path)
5151
self.old = Scan(old_path)
5252
self.options = options
53-
self.deltas = OrderedDict([
54-
('added', []),
55-
('removed', []),
56-
('moved', []),
57-
('modified', []),
58-
('unmodified', [])
59-
])
53+
self.deltas = []
6054
self.errors = []
6155

6256
if self.new.path != '' and self.old.path != '':
6357
self.determine_delta()
6458
self.determine_moved()
59+
# TODO: how can we test the sort order?
60+
# Sort deltas by score, descending, i.e., high > low.
61+
self.deltas.sort(key=lambda Delta: Delta.score, reverse=True)
6562

6663
def align_scan(self):
6764
"""
@@ -80,9 +77,9 @@ def align_scan(self):
8077

8178
def determine_delta(self):
8279
"""
83-
Given new and old scans, return an OrderedDict of Delta objects grouping
84-
the objects under the keys 'added', 'modified', 'removed' or 'unmodified'.
85-
Return None if no File objects can be loaded from either scan.
80+
Given new and old scans, return an list of Delta objects that can be
81+
sorted by their attributes, e.g., by Delta.score. Return None if no
82+
File objects can be loaded from either scan.
8683
"""
8784
# align scan and create our index
8885
self.align_scan()
@@ -104,7 +101,7 @@ def determine_delta(self):
104101
try:
105102
delta_old_files = old_index[path]
106103
except KeyError:
107-
self.deltas['added'].append(Delta(new_file, None, 'added'))
104+
self.deltas.append(Delta(new_file, None, 'added'))
108105
continue
109106

110107
# at this point, we have a delta_old_file.
@@ -113,11 +110,11 @@ def determine_delta(self):
113110
for f in delta_old_files:
114111
# TODO: make sure sha1 is NOT empty
115112
if new_file.sha1 == f.sha1:
116-
self.deltas['unmodified'].append(Delta(new_file, f, 'unmodified'))
113+
self.deltas.append(Delta(new_file, f, 'unmodified'))
117114
continue
118115
else:
119116
delta = Delta(new_file, f, 'modified')
120-
self.deltas['modified'].append(delta)
117+
self.deltas.append(delta)
121118

122119
# now time to find the added.
123120
for path, old_files in old_index.items():
@@ -131,7 +128,7 @@ def determine_delta(self):
131128
# This file already classified as 'modified' or 'unmodified' so do nothing
132129
new_index[path]
133130
except KeyError:
134-
self.deltas['removed'].append(Delta(None, old_file, 'removed'))
131+
self.deltas.append(Delta(None, old_file, 'removed'))
135132
continue
136133

137134
# make sure everything is accounted for
@@ -142,15 +139,15 @@ def determine_delta(self):
142139

143140
def determine_moved(self):
144141
"""
145-
Modify the OrderedDict of Delta objects by creating an index of
142+
Modify the list of Delta objects by creating an index of
146143
'removed' Delta objects and an index of 'added' Delta objects indexed
147144
by their 'sha1' attribute, identifying any unique pairs of Deltas in
148145
both indices with the same 'sha1' and File 'name' attributes, and
149146
converting each such pair of 'added' and 'removed' Delta objects to a
150147
'moved' Delta object.
151148
"""
152-
added = self.index_deltas('sha1', [i for i in self.deltas['added']])
153-
removed = self.index_deltas('sha1', [i for i in self.deltas['removed']])
149+
added = self.index_deltas('sha1', [i for i in self.deltas if i.category == 'added'])
150+
removed = self.index_deltas('sha1', [i for i in self.deltas if i.category == 'removed'])
154151

155152
# TODO: should it be iteritems() or items()
156153
for added_sha1, added_deltas in added.iteritems():
@@ -165,9 +162,9 @@ def update_deltas(self, added, removed):
165162
Convert the matched 'added' and 'removed' Delta objects to a combined
166163
'moved' Delta object and delete the 'added' and 'removed' objects.
167164
"""
168-
self.deltas.get('moved').append(Delta(added.new_file, removed.old_file, 'moved'))
169-
self.deltas.get('added').remove(added)
170-
self.deltas.get('removed').remove(removed)
165+
self.deltas.append(Delta(added.new_file, removed.old_file, 'moved'))
166+
self.deltas.remove(added)
167+
self.deltas.remove(removed)
171168

172169
def index_deltas(self, index_key='path', delta_list=[]):
173170
"""
@@ -197,51 +194,40 @@ def index_deltas(self, index_key='path', delta_list=[]):
197194
def get_stats(self):
198195
"""
199196
Given a list of Delta objects, return a 'counts' dictionary keyed by
200-
category -- i.e., the keys of the determine_delta() OrderedDict of
201-
Delta objects -- that contains the count as a value for each category.
197+
the Delta object's 'category' attribute that contains the count as a
198+
value for each category.
202199
"""
203200
added, modified, moved, removed, unmodified = 0, 0, 0, 0, 0
204201

205-
added = len(self.deltas['added'])
206-
modified = len(self.deltas['modified'])
207-
moved = len(self.deltas['moved'])
208-
removed = len(self.deltas['removed'])
209-
unmodified = len(self.deltas['unmodified'])
202+
added = len([i for i in self.deltas if i.category == 'added'])
203+
modified = len([i for i in self.deltas if i.category == 'modified'])
204+
moved = len([i for i in self.deltas if i.category == 'moved'])
205+
removed = len([i for i in self.deltas if i.category == 'removed'])
206+
unmodified = len([i for i in self.deltas if i.category == 'unmodified'])
210207

211208
return OrderedDict([('added', added), ('modified', modified), ('moved', moved), ('removed', removed), ('unmodified', unmodified)])
212209

213-
def to_dict(self):
214-
"""
215-
Given an OrderedDict of Delta objects, return an OrderedDict of Delta
216-
objects grouping the objects under the keys 'added', 'removed', 'moved',
217-
'modified' or 'unmodified'.
218-
"""
219-
return OrderedDict([
220-
('added', [d.to_dict() for d in self.deltas.get('added')]),
221-
('removed', [d.to_dict() for d in self.deltas.get('removed')]),
222-
('moved', [d.to_dict() for d in self.deltas.get('moved')]),
223-
('modified', [d.to_dict() for d in self.deltas.get('modified')]),
224-
('unmodified', [d.to_dict() for d in self.deltas.get('unmodified')]),
225-
])
226-
227210

228211
class Delta(object):
229212
"""
230213
A tuple reflecting a comparison of two files -- each of which is a File
231214
object -- and the category that characterizes the comparison:
232215
'added', 'modified', 'moved', 'removed' or 'unmodified'.
233216
"""
234-
def __init__(self, new_file=None, old_file=None, delta_type=None):
217+
def __init__(self, new_file=None, old_file=None, delta_type=None, score=0):
235218
self.new_file = new_file if new_file else File()
236219
self.old_file = old_file if old_file else File()
237220
self.category = delta_type if delta_type else ''
221+
self.score = score
238222

239223
# If a license change is detected, and depending on the nature of that change,
240224
# change the Delta object's 'category' attribute from 'modified' to
241225
# 'license change', 'license info removed' or 'license info added'.
242226
if self.category == 'modified':
243227
self._license_diff()
244228

229+
self.determine_score()
230+
245231
def _license_diff(self, cutoff_score=50):
246232
"""
247233
Compare the license details for a pair of 'new' and 'old' File objects
@@ -266,12 +252,33 @@ def _license_diff(self, cutoff_score=50):
266252
if new_keys != old_keys:
267253
self.category = 'license change'
268254

255+
def determine_score(self):
256+
"""
257+
Assign a score to each 'Delta' object by modifying the object's 'score'
258+
attribute based on the object's 'category' attribute.
259+
"""
260+
scores = {
261+
'added': 75,
262+
'license info added': 70,
263+
'license info removed': 65,
264+
'license change': 60,
265+
'modified': 50,
266+
'removed': 25,
267+
'moved': 0,
268+
'unmodified': 0
269+
}
270+
271+
self.score = scores.get(self.category, 0)
272+
269273
def to_dict(self):
270274
"""
271275
Check the 'category' attribute of the Delta object and return an
272-
OrderedDict comprising the 'category' and 'path' of the object.
276+
OrderedDict comprising the 'category', 'score' and 'path' of the object.
273277
"""
274-
delta = OrderedDict([('category', self.category)])
278+
delta = OrderedDict([
279+
('category', self.category),
280+
('score', self.score)
281+
])
275282

276283
if self.category == 'added':
277284
delta.update(OrderedDict([

src/deltacode/cli.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,16 @@ def write_csv(delta, result_file, all_delta_types=False):
4747
"""
4848
with open(result_file, 'wb') as out:
4949
csv_out = csv.writer(out)
50-
csv_out.writerow(['Type of delta', 'Path', 'Name', 'Type', 'Size', 'Old Path'])
50+
csv_out.writerow(['Type of delta', 'Score', 'Path', 'Name', 'Type', 'Size', 'Old Path'])
5151
for row in [(
5252
f.category,
53+
f.score,
5354
f.old_file.path if f.category == 'removed' else f.new_file.path,
5455
f.old_file.name if f.category == 'removed' else f.new_file.name,
5556
f.old_file.type if f.category == 'removed' else f.new_file.type,
5657
f.old_file.size if f.category == 'removed' else f.new_file.size,
5758
f.old_file.path if f.category == 'moved' else '')
58-
for d in delta.deltas for f in delta.deltas.get(d)]:
59+
for f in delta.deltas]:
5960
if all_delta_types is True:
6061
csv_out.writerow(row)
6162
elif row[0] != 'unmodified':

src/deltacode/utils.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,11 @@ def deltas(deltacode, all_delta_types=False):
4848
all Delta objects whose 'category' is 'unmodified' unless the user selects
4949
the '-a'/'--all' option.
5050
"""
51-
for category, deltas in deltacode.deltas.iteritems():
52-
for delta in deltas:
53-
if all_delta_types is True:
54-
yield delta.to_dict()
55-
elif delta.category != 'unmodified':
56-
yield delta.to_dict()
51+
for delta in deltacode.deltas:
52+
if all_delta_types is True:
53+
yield delta.to_dict()
54+
elif delta.category != 'unmodified':
55+
yield delta.to_dict()
5756

5857

5958
class AlignmentException(Exception):

tests/data/cli/1_file_moved.csv

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
Type of delta,Path,Name,Type,Size,Old Path
2-
moved,b/a4.py,a4.py,file,200,a/a4.py
3-
unmodified,a/a3.py,a3.py,file,200,
4-
unmodified,b/b4.py,b4.py,file,200,
5-
unmodified,a/a2.py,a2.py,file,200,
6-
unmodified,b/b2.py,b2.py,file,200,
7-
unmodified,b/b1.py,b1.py,file,200,
8-
unmodified,b/b3.py,b3.py,file,200,
9-
unmodified,a/a1.py,a1.py,file,200,
1+
Type of delta,Score,Path,Name,Type,Size,Old Path
2+
moved,0,b/a4.py,a4.py,file,200,a/a4.py
3+
unmodified,0,a/a3.py,a3.py,file,200,
4+
unmodified,0,b/b4.py,b4.py,file,200,
5+
unmodified,0,a/a2.py,a2.py,file,200,
6+
unmodified,0,b/b2.py,b2.py,file,200,
7+
unmodified,0,b/b1.py,b1.py,file,200,
8+
unmodified,0,b/b3.py,b3.py,file,200,
9+
unmodified,0,a/a1.py,a1.py,file,200,
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
Type of delta,Path,Name,Type,Size,Old Path
2-
moved,b/a4.py,a4.py,file,200,a/a4.py
1+
Type of delta,Score,Path,Name,Type,Size,Old Path
2+
moved,0,b/a4.py,a4.py,file,200,a/a4.py
Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
Type of delta,Path,Name,Type,Size,Old Path
2-
added,b/a4.py,a4.py,file,200,
3-
added,b/a4_copy.py,a4_copy.py,file,200,
4-
removed,a/a4.py,a4.py,file,200,
5-
unmodified,a/a3.py,a3.py,file,200,
6-
unmodified,b/b4.py,b4.py,file,200,
7-
unmodified,a/a2.py,a2.py,file,200,
8-
unmodified,b/b2.py,b2.py,file,200,
9-
unmodified,b/b1.py,b1.py,file,200,
10-
unmodified,b/b3.py,b3.py,file,200,
11-
unmodified,a/a1.py,a1.py,file,200,
1+
Type of delta,Score,Path,Name,Type,Size,Old Path
2+
added,75,b/a4.py,a4.py,file,200,
3+
added,75,b/a4_copy.py,a4_copy.py,file,200,
4+
removed,25,a/a4.py,a4.py,file,200,
Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
Type of delta,Path,Name,Type,Size,Old Path
2-
added,b/a4.py,a4.py,file,200,
3-
added,c/a4.py,a4.py,file,200,
4-
removed,a/a4.py,a4.py,file,200,
5-
unmodified,a/a3.py,a3.py,file,200,
6-
unmodified,b/b4.py,b4.py,file,200,
7-
unmodified,a/a2.py,a2.py,file,200,
8-
unmodified,b/b2.py,b2.py,file,200,
9-
unmodified,b/b1.py,b1.py,file,200,
10-
unmodified,b/b3.py,b3.py,file,200,
11-
unmodified,a/a1.py,a1.py,file,200,
1+
Type of delta,Score,Path,Name,Type,Size,Old Path
2+
added,75,b/a4.py,a4.py,file,200,
3+
added,75,c/a4.py,a4.py,file,200,
4+
removed,25,a/a4.py,a4.py,file,200,

tests/data/cli/added1.csv

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,2 @@
1-
Type of delta,Path,Name,Type,Size,Old Path
2-
added,a/a5.py,a5.py,file,200,
3-
unmodified,a/a3.py,a3.py,file,200,
4-
unmodified,b/b4.py,b4.py,file,200,
5-
unmodified,a/a2.py,a2.py,file,200,
6-
unmodified,b/b2.py,b2.py,file,200,
7-
unmodified,b/b1.py,b1.py,file,200,
8-
unmodified,b/b3.py,b3.py,file,200,
9-
unmodified,a/a4.py,a4.py,file,200,
10-
unmodified,a/a1.py,a1.py,file,200,
1+
Type of delta,Score,Path,Name,Type,Size,Old Path
2+
added,75,a/a5.py,a5.py,file,200,
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
Type of delta,Path,Name,Type,Size,Old Path
2-
license info added,some/path/a/a1.py,a1.py,file,350,
3-
unmodified,some/path/b/b1.py,b1.py,file,290,
1+
Type of delta,Score,Path,Name,Type,Size,Old Path
2+
license info added,70,some/path/a/a1.py,a1.py,file,350,
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
Type of delta,Path,Name,Type,Size,Old Path
2-
license info added,some/path/a/a1.py,a1.py,file,350,
3-
unmodified,some/path/b/b1.py,b1.py,file,290,
1+
Type of delta,Score,Path,Name,Type,Size,Old Path
2+
license info added,70,some/path/a/a1.py,a1.py,file,350,

0 commit comments

Comments
 (0)