xref: /aosp_15_r20/external/autotest/frontend/tko/csv_encoder.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
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