3030from deltacode .models import File
3131from deltacode .models import Scan
3232from deltacode import utils
33+ from scancode .resource import VirtualCodebase
3334
3435
3536from pkg_resources import get_distribution , DistributionNotFound
@@ -48,14 +49,25 @@ class DeltaCode(object):
4849 the form of File objects) contained in those scans.
4950 """
5051 def __init__ (self , new_path , old_path , options ):
51- self .new = Scan (new_path )
52- self .old = Scan (old_path )
52+ if new_path and old_path :
53+ self .codebase1 = VirtualCodebase (new_path )
54+ self .codebase2 = VirtualCodebase (old_path )
55+ self .new_files_count = 0 #keeps the count of the new file
56+ self .old_files_count = 0 #keeps the count of old files
57+ self .new_files = [] # a list of [[new file1:Original path],[new file2:Original Path],...]
58+ self .old_files = [] # a list of [[old file1:Original path],[old file2:Original Path],...]
59+ self .new_files_fingerprint = dict () # map of { {new_file1:fingerprint},{new_file2:fingerprint},...} it will be needed when we need the fingerprints
60+ self .old_files_fingerprint = dict () # map of { {old_file1:fingerprint},{old_file2:fingerprint},...}
5361 self .options = options
5462 self .deltas = []
5563 self .errors = []
56- self .stats = Stat (self .new .files_count , self .old .files_count )
64+ self .enumerate_files_from_codebases ()
65+ self .stats = Stat (self .new_files_count , self .old_files_count )
66+
67+ if new_path != None and old_path != None :
5768
58- if self .new .path != '' and self .old .path != '' :
69+ self .new_files_errors = []
70+ self .old_files_errors = []
5971 self .determine_delta ()
6072 self .determine_moved ()
6173 self .license_diff ()
@@ -67,6 +79,46 @@ def __init__(self, new_path, old_path, options):
6779 self .deltas .sort (key = lambda Delta : Delta .factors , reverse = False )
6880 self .deltas .sort (key = lambda Delta : Delta .score , reverse = True )
6981
82+ def get_files (self ,codebase ,is_new ):
83+ """
84+ Walk through the codebase, then generate the resources it(including all files and its directories)
85+ then we enumerate over this generated codebase to get file, and directories as (obj)
86+ Now during the time of enumeration we append files in the self.new_files list and incremants out self.new_files_count
87+ Similarly for old_files.
88+ Now , we also maintain a map which maps from object path to its fingerprint.
89+ This map will be required when we calculate the hamming distances and compare similarity.
90+ """
91+ resources = codebase .walk_filtered (topdown = True )
92+ for i ,obj in enumerate (resources ):
93+ if is_new :
94+ if obj .is_file :
95+ # append in the new_files
96+ self .new_files .append ([obj ,'' ])
97+ # increment the new files count
98+ self .new_files_count += 1
99+ try :
100+ self .new_files_fingerprint [obj .path ] = obj .fingerprint
101+ except AttributeError :
102+ self .new_files_fingerprint [obj .path ] = None
103+ else :
104+ if obj .is_file :
105+ # append in the old files
106+ self .old_files .append ([obj ,'' ])
107+ # increment the old files count
108+ self .old_files_count += 1
109+ try :
110+ self .old_files_fingerprint [obj .path ] = obj .fingerprint
111+ except AttributeError :
112+ self .old_files_fingerprint [obj .path ] = None
113+
114+ def enumerate_files_from_codebases (self ):
115+ """
116+ An method which call the utility function get_files for generating the codebase
117+ """
118+ self .get_files (self .codebase1 ,is_new = True )
119+ self .get_files (self .codebase2 ,is_new = False )
120+
121+
70122 def align_scans (self ):
71123 """
72124 Seek to align the paths of a pair of files (File objects) in the pair
@@ -75,12 +127,15 @@ def align_scans(self):
75127 which calls utils.align_trees().
76128 """
77129 try :
78- utils .fix_trees (self .new . files , self .old . files )
130+ utils .fix_trees (self .new_files , self .old_files )
79131 except utils .AlignmentException :
80- for f in self .new .files :
81- f .original_path = f .path
82- for f in self .old .files :
83- f .original_path = f .path
132+ # self.new_files is a list of type [[ScannedResourceObject,originalPath],...]
133+ # initially all original path are set to empty string
134+ # so this part actually sets the original paths
135+ for f in self .new_files :
136+ f [1 ] = f [0 ].path
137+ for f in self .old_files :
138+ f [1 ] = f [0 ].path
84139
85140 def similarity (self ):
86141 """
@@ -93,12 +148,13 @@ def similarity(self):
93148 for delta in self .deltas :
94149 if delta .new_file == None or delta .old_file == None :
95150 continue
96- new_fingerprint = delta .new_file .fingerprint
97- old_fingerprint = delta .old_file .fingerprint
151+ # this extracts the fingerprint corresponding to the particular file path
152+ new_fingerprint = self .new_files_fingerprint [delta .new_file .path ]
153+ old_fingerprint = self .old_files_fingerprint [delta .old_file .path ]
98154 if new_fingerprint == None or old_fingerprint == None :
99155 continue
100- new_fingerprint = utils .bitarray_from_hex (delta .new_file .fingerprint )
101- old_fingerprint = utils .bitarray_from_hex (delta .old_file .fingerprint )
156+ new_fingerprint = utils .bitarray_from_hex (self . new_files_fingerprint [ delta .new_file .path ] )
157+ old_fingerprint = utils .bitarray_from_hex (self . old_files_fingerprint [ delta .old_file .path ] )
102158 hamming_distance = utils .hamming_distance (new_fingerprint , old_fingerprint )
103159 if hamming_distance > 0 and hamming_distance <= SIMILARITY_LIMIT :
104160 delta .score += hamming_distance
@@ -112,8 +168,9 @@ def determine_delta(self):
112168 """
113169 # align scan and create our index
114170 self .align_scans ()
115- new_index = self .new .index_files ()
116- old_index = self .old .index_files ()
171+ # returns file index wrt to old and new files
172+ new_index = utils .index_files (self .new_files )
173+ old_index = utils .index_files (self .old_files )
117174
118175 # gathering counts to ensure no files lost or missing from our 'deltas' set
119176 new_visited , old_visited = 0 , 0
@@ -172,12 +229,12 @@ def determine_delta(self):
172229 continue
173230
174231 # make sure everything is accounted for
175- if new_visited != self .new . files_count :
232+ if new_visited != self .new_files_count :
176233 self .errors .append (
177234 'DeltaCode Warning: new_visited({}) != new_total({}). Assuming old scancode format.' .format (new_visited , self .new .files_count )
178235 )
179236
180- if old_visited != self .old . files_count :
237+ if old_visited != self .old_files_count :
181238 self .errors .append (
182239 'DeltaCode Warning: old_visited({}) != old_total({}). Assuming old scancode format.' .format (old_visited , self .old .files_count )
183240 )
@@ -323,7 +380,99 @@ def is_added(self):
323380 if self .new_file and not self .old_file :
324381 return True
325382
326- def to_dict (self ):
383+ def copyrights_to_dict (self ,file ):
384+ """
385+ Given a Copyright object, return an OrderedDict with the full
386+ set of fields from the ScanCode 'copyrights' value.
387+ """
388+ copyrightC = []
389+ try :
390+ copyrightC = file .copyrights
391+ except AttributeError :
392+ # arises when the ScannedResource do not have any license attribute
393+ return []
394+ if len (copyrightC ) == 0 :
395+ return []
396+ if isinstance (copyrightC [0 ],OrderedDict ):
397+ # all the copyright are in correct format
398+ all_copyrights = []
399+ for i in range (len (copyrightC )):
400+ # we iterate over all the copyrights
401+ statements = copyrightC [i ].get ("statements" )
402+ holders = copyrightC [i ].get ("holders" )
403+ d = OrderedDict ([
404+ ('statements' , statements ),
405+ ('holders' , holders )
406+ ])
407+ all_copyrights .append (d )
408+
409+ return all_copyrights
410+
411+ def licenses_to_dict (self ,file ):
412+ """
413+ Given a License object, return an OrderedDict with the full
414+ set of fields from the ScanCode 'license' value.
415+ """
416+ licenseL = []
417+ try :
418+ licenseL = file .license
419+ except AttributeError :
420+ # arises when the ScannedResource do not have any license attribute
421+ return []
422+
423+ if len (licenseL ) == 0 :
424+ return []
425+ if isinstance (licenseL [0 ],OrderedDict ):
426+ # the licenses are in the correct format
427+ all_licenses = []
428+ for i in range (len (licenseL )):
429+ # we iterate over all the licenses
430+ d = OrderedDict ([
431+ ('key' , licenseL [i ]["key" ]),
432+ ('score' , licenseL [i ]["score" ]),
433+ ('short_name' , licenseL [i ]["short_name" ]),
434+ ('category' , licenseL [i ]["category" ]),
435+ ('owner' , licenseL [i ]["owner" ])
436+ ])
437+ all_licenses .append (d )
438+ return all_licenses
439+
440+ def new_file_to_dict (self ,deltacode ):
441+ # if self.new_file is not empty we return the new file attributes
442+ if self .new_file :
443+ return OrderedDict ([
444+ ("path" ,self .new_file .path ),
445+ ("type" ,self .new_file .type ),
446+ ("name" ,self .new_file .name ),
447+ ("size" ,self .new_file .size ),
448+ ("sha1" ,self .new_file .sha1 ),
449+ ("fingerprint" ,deltacode .new_files_fingerprint [self .new_file .path ]),
450+ ("original_path" ,self .new_file .path ),
451+ # since license itself has many sub fields so we obtain it from another utility function
452+ ("licenses" ,self .licenses_to_dict (self .new_file )),
453+ # since copyright itself has many sub fields so we obtain it from another utility function
454+ ("copyrights" ,self .copyrights_to_dict (self .new_file ))
455+ ])
456+
457+
458+ def old_file_to_dict (self ,deltacode ):
459+ if self .old_file :
460+ return OrderedDict ([
461+ ("path" ,self .old_file .path ),
462+ ("type" ,self .old_file .type ),
463+ ("name" ,self .old_file .name ),
464+ ("size" ,self .old_file .size ),
465+ ("sha1" ,self .old_file .sha1 ),
466+ ("fingerprint" ,deltacode .old_files_fingerprint [self .old_file .path ]),
467+ ("original_path" ,self .old_file .path ),
468+ # since license itself has many sub fields so we obtain it from another utility function
469+ ("licenses" ,self .licenses_to_dict (self .old_file )),
470+ # since copyright itself has many sub fields so we obtain it from another utility function
471+ ("copyrights" ,self .copyrights_to_dict (self .old_file ))
472+ ])
473+
474+
475+ def to_dict (self ,deltacode ):
327476 """
328477 Return an OrderedDict comprising the 'factors', 'score' and new and old
329478 'path' attributes of the object.
@@ -342,8 +491,10 @@ def to_dict(self):
342491 ('status' , self .status ),
343492 ('factors' , self .factors ),
344493 ('score' , self .score ),
345- ('new' , new_file ),
346- ('old' , old_file ),
494+ # receives the detail of the new file
495+ ('new' , self .new_file_to_dict (deltacode )),
496+ # receives the details of the old file
497+ ('old' , self .old_file_to_dict (deltacode )),
347498 ])
348499
349500class Stat (object ):
0 commit comments