| #!/usr/bin/env python3 |
| # Copyright 2023 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Scans components/autofill/core/browser/field_types.h for FieldTypes |
| and updates histograms that are calculated from this enum. |
| """ |
| |
| import optparse |
| import os |
| import re |
| import sys |
| |
| sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common')) |
| import path_util |
| |
| import update_histogram_enum |
| |
| FIELD_TYPES_PATH = 'components/autofill/core/browser/field_types.h' |
| FIELD_PREDICTION_GROUPS_PATH = \ |
| 'components/autofill/core/browser/metrics/prediction_quality_metrics.cc' |
| |
| |
| def ReadEnum(filename, first_line, last_line_exclusive): |
| """Extracts an enum from a file. |
| |
| Args: |
| filename: name of file to read. |
| first_line: prefix of a line where extracting values starts. Leading |
| whitespaces are ignored. |
| last_line_exclusive: prefix of a line before which extraction stops. |
| This means that the line matching this parameter is not considered |
| a valid enum entry that is returned. Leading whitespaces are ignored. |
| |
| Returns: |
| Dictionary from enum ids (integers) to names (strings). |
| """ |
| |
| # Read the enum FieldType as a list of lines. |
| before_enum = True |
| content = [] |
| with open(path_util.GetInputFile(filename)) as f: |
| for line in f.readlines(): |
| stripped_line = line.strip() |
| # Search for beginning of enum. |
| if before_enum: |
| if stripped_line.startswith(first_line): |
| before_enum = False |
| continue |
| # Terminate at end of enum. |
| if stripped_line.startswith(last_line_exclusive): |
| break |
| content.append(stripped_line) |
| |
| ENUM_REGEX = re.compile( |
| r"""^\s*?(\w+)\s+= # capture the enum name |
| \s+(\d+)(?:,.*)?$ # capture the id |
| """, re.VERBOSE) |
| |
| enums = {} |
| for line in content: |
| enum_match = ENUM_REGEX.search(line) |
| if enum_match: |
| enum_name = enum_match.group(1) |
| enum_id = int(enum_match.group(2)) |
| enums[enum_id] = enum_name |
| |
| return enums |
| |
| |
| def ReadFieldTypes(filename): |
| return ReadEnum(filename, 'enum FieldType {', 'MAX_VALID_FIELD_TYPE =') |
| |
| |
| def ReadFieldPredictionGroups(filename): |
| result = ReadEnum(filename, 'enum FieldTypeGroupForMetrics {', |
| 'NUM_FIELD_TYPE_GROUPS_FOR_METRICS') |
| # Strip the GROUP_ prefix because it only adds clutter. |
| return {k: v.replace('GROUP_', '') for k, v in result.items()} |
| |
| |
| def GenerateAutofilledFieldUserEditingStatusByFieldType(server_field_types): |
| result = {} |
| for enum_id, enum_name in server_field_types.items(): |
| result[16 * enum_id + 0] = f'{enum_name}: edited' |
| result[16 * enum_id + 1] = f'{enum_name}: accepted' |
| return result |
| |
| |
| def GenerateAutofillFieldPredictionQualityByFieldType(): |
| groups = ReadFieldPredictionGroups(FIELD_PREDICTION_GROUPS_PATH) |
| result = {} |
| for enum_id, enum_name in groups.items(): |
| result[256 * enum_id + 0] = f'{enum_name}: True Positive' |
| result[256 * enum_id + 1] = f'{enum_name}: True Negative (Ambiguous)' |
| result[256 * enum_id + 2] = f'{enum_name}: True Negative (Unknown)' |
| result[256 * enum_id + 3] = f'{enum_name}: True Negative (Empty)' |
| result[256 * enum_id + 4] = f'{enum_name}: False Positive (Mismatch)' |
| result[256 * enum_id + 5] = f'{enum_name}: False Positive (Ambiguous)' |
| result[256 * enum_id + 6] = f'{enum_name}: False Positive (Unknown)' |
| result[256 * enum_id + 7] = f'{enum_name}: False Positive (Empty)' |
| result[256 * enum_id + 8] = f'{enum_name}: False Negative (Mismatch)' |
| result[256 * enum_id + 9] = f'{enum_name}: False Negative (Unknown)' |
| return result |
| |
| |
| def GenerateAutofillDataUtilizationByFieldType(field_types): |
| result = {} |
| for enum_id, enum_name in field_types.items(): |
| result[64 * enum_id + |
| 0] = f'{enum_name}: Not autofilled or autofilled value edited' |
| result[64 * enum_id + 1] = f'{enum_name}: Autofilled value accepted' |
| return result |
| |
| |
| def GenerateFillingAcceptanceByFieldType(server_field_types): |
| result = {} |
| for enum_id, enum_name in server_field_types.items(): |
| result[4 * enum_id + 0] = f'{enum_name}: Ignored' |
| result[4 * enum_id + 1] = f'{enum_name}: Accepted' |
| return result |
| |
| |
| if __name__ == '__main__': |
| server_field_types = ReadFieldTypes(FIELD_TYPES_PATH) |
| |
| update_histogram_enum.UpdateHistogramFromDict( |
| 'tools/metrics/histograms/metadata/autofill/enums.xml', |
| 'AutofillFieldType', server_field_types, FIELD_TYPES_PATH, |
| os.path.basename(__file__)) |
| |
| update_histogram_enum.UpdateHistogramFromDict( |
| 'tools/metrics/histograms/metadata/autofill/enums.xml', |
| 'AutofilledFieldUserEditingStatusByFieldType', |
| GenerateAutofilledFieldUserEditingStatusByFieldType(server_field_types), |
| FIELD_TYPES_PATH, os.path.basename(__file__)) |
| |
| update_histogram_enum.UpdateHistogramFromDict( |
| 'tools/metrics/histograms/metadata/autofill/enums.xml', |
| 'AutofillFieldPredictionQualityByFieldType', |
| GenerateAutofillFieldPredictionQualityByFieldType(), |
| FIELD_PREDICTION_GROUPS_PATH, os.path.basename(__file__)) |
| |
| update_histogram_enum.UpdateHistogramFromDict( |
| 'tools/metrics/histograms/metadata/autofill/enums.xml', |
| 'AutofillDataUtilizationByFieldType', |
| GenerateAutofillDataUtilizationByFieldType(server_field_types), |
| FIELD_TYPES_PATH, os.path.basename(__file__)) |
| |
| update_histogram_enum.UpdateHistogramFromDict( |
| 'tools/metrics/histograms/metadata/autofill/enums.xml', |
| 'FillingAcceptanceByFieldType', |
| GenerateFillingAcceptanceByFieldType(server_field_types), |
| FIELD_TYPES_PATH, os.path.basename(__file__)) |
| |
| update_histogram_enum.UpdateHistogramFromDict( |
| 'tools/metrics/histograms/metadata/autofill/histograms.xml', |
| 'AutofillFieldType', |
| server_field_types, |
| FIELD_TYPES_PATH, |
| os.path.basename(__file__), |
| update_comment=False) |