66import logging
77import os
88from xml .dom import minidom
9- from math import floor
109
1110import requests
1211from requests .packages .urllib3 import util as urllib3_util
2322
2423
2524class _Retry (urllib3_util .Retry ):
26-
2725 def is_forced_retry (self , method , status_code ):
2826 return status_code >= BAD_REQUEST
2927
@@ -76,26 +74,37 @@ def strip_prefix(line, prefix):
7674 return filename
7775
7876
79- def merge_reports (report_list ):
80- """Merges together several report structures from parse_report_file"""
81- final_report = {
82- 'language' : "python" ,
83- 'fileReports' : []
84- }
77+ def merge_and_round_reports (report_list ):
78+ """Merges together several report structures from parse_report_file (and rounds all values)"""
79+
80+ if len (report_list ) == 1 :
81+ final_report = report_list [0 ]
82+ else :
83+ final_report = {
84+ 'language' : "python" ,
85+ 'fileReports' : []
86+ }
8587
86- for report in report_list :
87- # First, merge together detailed report structures
88- # This assumes no overlap
89- # TODO: What should we do if there is a file listed multiple times?
90- final_report ['fileReports' ] += report ['fileReports' ]
88+ total_lines = 0
89+ for report in report_list :
90+ # First, merge together detailed report structures
91+ # This assumes no overlap
92+ # TODO: What should we do if there is a file listed multiple times?
93+ final_report ['fileReports' ] += report ['fileReports' ]
94+ total_lines += report ['codeLines' ]
9195
92- # Gather all per-file coverage
93- total_coverages = []
94- for fileentry in final_report ['fileReports' ]:
95- total_coverages += [ fileentry [ 'total' ]]
96+ # Coverage weighted average (by number of lines of code) of all files
97+ average_sum = 0
98+ for file_entry in final_report ['fileReports' ]:
99+ average_sum += file_entry [ 'total' ] * file_entry [ 'codeLines' ]
96100
97- # And average
98- final_report ['total' ] = int (sum (total_coverages )/ len (total_coverages ))
101+ final_report ['total' ] = average_sum / total_lines
102+ final_report ['codeLines' ] = total_lines
103+
104+ # Round all total values
105+ for file_entry in final_report ['fileReports' ]:
106+ file_entry ['total' ] = int (file_entry ['total' ])
107+ final_report ['total' ] = int (final_report ['total' ])
99108
100109 return final_report
101110
@@ -105,9 +114,9 @@ def parse_report_file(report_file, git_directory):
105114 :param report_file:
106115 """
107116
108- # Convert decimal string to floored int percent value
117+ # Convert decimal string to decimal percent value
109118 def percent (s ):
110- return int ( floor ( float (s ) * 100 ))
119+ return float (s ) * 100
111120
112121 # Parse the XML into the format expected by the API
113122 report_xml = minidom .parse (report_file )
@@ -120,20 +129,25 @@ def percent(s):
120129
121130 sources = [x .firstChild .nodeValue for x in report_xml .getElementsByTagName ('source' )]
122131 classes = report_xml .getElementsByTagName ('class' )
132+ total_lines = 0
123133 for cls in classes :
134+ lines = cls .getElementsByTagName ('line' )
135+ total_lines += len (lines )
124136 file_report = {
125137 'filename' : generate_filename (sources , cls .attributes ['filename' ].value , git_directory ),
126138 'total' : percent (cls .attributes ['line-rate' ].value ),
139+ 'codeLines' : len (lines ),
127140 'coverage' : {},
128141 }
129- lines = cls .getElementsByTagName ('line' )
130142 for line in lines :
131143 hits = int (line .attributes ['hits' ].value )
132144 if hits >= 1 :
133145 # The API assumes 0 if a line is missing
134146 file_report ['coverage' ][line .attributes ['number' ].value ] = hits
135147 report ['fileReports' ] += [file_report ]
136148
149+ report ['codeLines' ] = total_lines
150+
137151 return report
138152
139153
@@ -197,7 +211,7 @@ def run():
197211 logging .info ("Parsing report file %s..." , rfile )
198212 reports .append (parse_report_file (rfile , args .directory ))
199213
200- report = merge_reports (reports )
214+ report = merge_and_round_reports (reports )
201215
202216 logging .info ("Uploading report..." )
203217 upload_report (report , CODACY_PROJECT_TOKEN , args .commit )
0 commit comments