1*9c5db199SXin Liimport csv 2*9c5db199SXin Liimport django.http 3*9c5db199SXin Liimport common 4*9c5db199SXin Lifrom autotest_lib.frontend.afe import rpc_utils 5*9c5db199SXin Li 6*9c5db199SXin Liclass CsvEncoder(object): 7*9c5db199SXin Li def __init__(self, request, response): 8*9c5db199SXin Li self._request = request 9*9c5db199SXin Li self._response = response 10*9c5db199SXin Li self._output_rows = [] 11*9c5db199SXin Li 12*9c5db199SXin Li 13*9c5db199SXin Li def _append_output_row(self, row): 14*9c5db199SXin Li self._output_rows.append(row) 15*9c5db199SXin Li 16*9c5db199SXin Li 17*9c5db199SXin Li def _build_response(self): 18*9c5db199SXin Li response = django.http.HttpResponse(mimetype='text/csv') 19*9c5db199SXin Li response['Content-Disposition'] = ( 20*9c5db199SXin Li 'attachment; filename=tko_query.csv') 21*9c5db199SXin Li writer = csv.writer(response) 22*9c5db199SXin Li writer.writerows(self._output_rows) 23*9c5db199SXin Li return response 24*9c5db199SXin Li 25*9c5db199SXin Li 26*9c5db199SXin Li def encode(self): 27*9c5db199SXin Li raise NotImplementedError 28*9c5db199SXin Li 29*9c5db199SXin Li 30*9c5db199SXin Liclass UnhandledMethodEncoder(CsvEncoder): 31*9c5db199SXin Li def encode(self): 32*9c5db199SXin Li return rpc_utils.raw_http_response( 33*9c5db199SXin Li 'Unhandled method %s (this indicates a bug)\r\n' % 34*9c5db199SXin Li self._request['method']) 35*9c5db199SXin Li 36*9c5db199SXin Li 37*9c5db199SXin Liclass SpreadsheetCsvEncoder(CsvEncoder): 38*9c5db199SXin Li def _total_index(self, group, num_columns): 39*9c5db199SXin Li row_index, column_index = group['header_indices'] 40*9c5db199SXin Li return row_index * num_columns + column_index 41*9c5db199SXin Li 42*9c5db199SXin Li 43*9c5db199SXin Li def _group_string(self, group): 44*9c5db199SXin Li result = '%s / %s' % (group['pass_count'], group['complete_count']) 45*9c5db199SXin Li if group['incomplete_count'] > 0: 46*9c5db199SXin Li result += ' (%s incomplete)' % group['incomplete_count'] 47*9c5db199SXin Li if 'extra_info' in group: 48*9c5db199SXin Li result = '\n'.join([result] + group['extra_info']) 49*9c5db199SXin Li return result 50*9c5db199SXin Li 51*9c5db199SXin Li 52*9c5db199SXin Li def _build_value_table(self): 53*9c5db199SXin Li value_table = [''] * self._num_rows * self._num_columns 54*9c5db199SXin Li for group in self._response['groups']: 55*9c5db199SXin Li total_index = self._total_index(group, self._num_columns) 56*9c5db199SXin Li value_table[total_index] = self._group_string(group) 57*9c5db199SXin Li return value_table 58*9c5db199SXin Li 59*9c5db199SXin Li 60*9c5db199SXin Li def _header_string(self, header_value): 61*9c5db199SXin Li return '/'.join(header_value) 62*9c5db199SXin Li 63*9c5db199SXin Li 64*9c5db199SXin Li def _process_value_table(self, value_table, row_headers): 65*9c5db199SXin Li total_index = 0 66*9c5db199SXin Li for row_index in range(self._num_rows): 67*9c5db199SXin Li row_header = self._header_string(row_headers[row_index]) 68*9c5db199SXin Li row_end_index = total_index + self._num_columns 69*9c5db199SXin Li row_values = value_table[total_index:row_end_index] 70*9c5db199SXin Li self._append_output_row([row_header] + row_values) 71*9c5db199SXin Li total_index += self._num_columns 72*9c5db199SXin Li 73*9c5db199SXin Li 74*9c5db199SXin Li def encode(self): 75*9c5db199SXin Li header_values = self._response['header_values'] 76*9c5db199SXin Li assert len(header_values) == 2 77*9c5db199SXin Li row_headers, column_headers = header_values 78*9c5db199SXin Li self._num_rows, self._num_columns = (len(row_headers), 79*9c5db199SXin Li len(column_headers)) 80*9c5db199SXin Li 81*9c5db199SXin Li value_table = self._build_value_table() 82*9c5db199SXin Li 83*9c5db199SXin Li first_line = [''] + [self._header_string(header_value) 84*9c5db199SXin Li for header_value in column_headers] 85*9c5db199SXin Li self._append_output_row(first_line) 86*9c5db199SXin Li self._process_value_table(value_table, row_headers) 87*9c5db199SXin Li 88*9c5db199SXin Li return self._build_response() 89*9c5db199SXin Li 90*9c5db199SXin Li 91*9c5db199SXin Liclass TableCsvEncoder(CsvEncoder): 92*9c5db199SXin Li def __init__(self, request, response): 93*9c5db199SXin Li super(TableCsvEncoder, self).__init__(request, response) 94*9c5db199SXin Li self._column_specs = request['columns'] 95*9c5db199SXin Li 96*9c5db199SXin Li 97*9c5db199SXin Li def _format_row(self, row_object): 98*9c5db199SXin Li """Extract data from a row object into a list of strings""" 99*9c5db199SXin Li return [row_object.get(field) for field, name in self._column_specs] 100*9c5db199SXin Li 101*9c5db199SXin Li 102*9c5db199SXin Li def _encode_table(self, row_objects): 103*9c5db199SXin Li self._append_output_row([column_spec[1] # header row 104*9c5db199SXin Li for column_spec in self._column_specs]) 105*9c5db199SXin Li for row_object in row_objects: 106*9c5db199SXin Li self._append_output_row(self._format_row(row_object)) 107*9c5db199SXin Li return self._build_response() 108*9c5db199SXin Li 109*9c5db199SXin Li 110*9c5db199SXin Li def encode(self): 111*9c5db199SXin Li return self._encode_table(self._response) 112*9c5db199SXin Li 113*9c5db199SXin Li 114*9c5db199SXin Liclass GroupedTableCsvEncoder(TableCsvEncoder): 115*9c5db199SXin Li def encode(self): 116*9c5db199SXin Li return self._encode_table(self._response['groups']) 117*9c5db199SXin Li 118*9c5db199SXin Li 119*9c5db199SXin Liclass StatusCountTableCsvEncoder(GroupedTableCsvEncoder): 120*9c5db199SXin Li _PASS_RATE_FIELD = '_test_pass_rate' 121*9c5db199SXin Li 122*9c5db199SXin Li def __init__(self, request, response): 123*9c5db199SXin Li super(StatusCountTableCsvEncoder, self).__init__(request, response) 124*9c5db199SXin Li # inject a more sensible field name for test pass rate 125*9c5db199SXin Li for column_spec in self._column_specs: 126*9c5db199SXin Li field, name = column_spec 127*9c5db199SXin Li if name == 'Test pass rate': 128*9c5db199SXin Li column_spec[0] = self._PASS_RATE_FIELD 129*9c5db199SXin Li break 130*9c5db199SXin Li 131*9c5db199SXin Li 132*9c5db199SXin Li def _format_pass_rate(self, row_object): 133*9c5db199SXin Li result = '%s / %s' % (row_object['pass_count'], 134*9c5db199SXin Li row_object['complete_count']) 135*9c5db199SXin Li incomplete_count = row_object['incomplete_count'] 136*9c5db199SXin Li if incomplete_count: 137*9c5db199SXin Li result += ' (%s incomplete)' % incomplete_count 138*9c5db199SXin Li return result 139*9c5db199SXin Li 140*9c5db199SXin Li 141*9c5db199SXin Li def _format_row(self, row_object): 142*9c5db199SXin Li row_object[self._PASS_RATE_FIELD] = self._format_pass_rate(row_object) 143*9c5db199SXin Li return super(StatusCountTableCsvEncoder, self)._format_row(row_object) 144*9c5db199SXin Li 145*9c5db199SXin Li 146*9c5db199SXin Li_ENCODER_MAP = { 147*9c5db199SXin Li 'get_latest_tests' : SpreadsheetCsvEncoder, 148*9c5db199SXin Li 'get_test_views' : TableCsvEncoder, 149*9c5db199SXin Li 'get_group_counts' : GroupedTableCsvEncoder, 150*9c5db199SXin Li} 151*9c5db199SXin Li 152*9c5db199SXin Li 153*9c5db199SXin Lidef _get_encoder_class(request): 154*9c5db199SXin Li method = request['method'] 155*9c5db199SXin Li if method in _ENCODER_MAP: 156*9c5db199SXin Li return _ENCODER_MAP[method] 157*9c5db199SXin Li if method == 'get_status_counts': 158*9c5db199SXin Li if 'columns' in request: 159*9c5db199SXin Li return StatusCountTableCsvEncoder 160*9c5db199SXin Li return SpreadsheetCsvEncoder 161*9c5db199SXin Li return UnhandledMethodEncoder 162*9c5db199SXin Li 163*9c5db199SXin Li 164*9c5db199SXin Lidef encoder(request, response): 165*9c5db199SXin Li EncoderClass = _get_encoder_class(request) 166*9c5db199SXin Li return EncoderClass(request, response) 167