Chromium Code Reviews| Index: tools/metrics/rappor/pretty_print.py |
| diff --git a/tools/metrics/rappor/pretty_print.py b/tools/metrics/rappor/pretty_print.py |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..8141d416f70d784afc62c1ddb730fecba5822abd |
| --- /dev/null |
| +++ b/tools/metrics/rappor/pretty_print.py |
| @@ -0,0 +1,162 @@ |
| +#!/usr/bin/env python |
| +# Copyright 2015 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +import logging |
| +import sys |
| +import os |
| + |
| +# Import the metrics/common module for pretty print xml. |
| +sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common')) |
| +import models |
| +import presubmit_util |
| + |
| + |
| +# Model definitions for rappor.xml content |
| +summaryType = models.TextNodeType('summary') |
| + |
| +parametersType = models.ObjectNodeType('parameters', |
|
Alexei Svitkine (slow)
2015/02/13 14:36:37
Nit: camelCase isn't a thing in Python style...
I
Steven Holte
2015/02/14 01:11:18
Done.
|
| + int_attributes=[ |
| + 'num-cohorts', |
| + 'bytes', |
| + 'hash-functions', |
| + ], |
| + float_attributes=[ |
| + 'fake-prob', |
| + 'fake-one-prob', |
| + 'one-coin-prob', |
| + 'zero-coin-prob', |
| + ], |
| + string_attributes=[ |
| + 'reporting-level' |
| + ]) |
| + |
| +rapporParametersType = models.ObjectNodeType('rappor-parameters', |
| + extra_newlines=(1, 1, 1), |
| + string_attributes=['name'], |
| + children=[ |
| + models.ChildType('summary', summaryType, False), |
| + models.ChildType('parameters', parametersType, False), |
| + ]) |
| + |
| +rapporParameterTypesType = models.ObjectNodeType('rappor-parameter-types', |
| + extra_newlines=(1, 1, 1), |
| + dont_indent=True, |
| + children=[ |
| + models.ChildType('types', rapporParametersType, True), |
| + ]) |
| + |
| +ownerType = models.TextNodeType('owner', single_line=True) |
| + |
| +rapporMetricType = models.ObjectNodeType('rappor-metric', |
| + extra_newlines=(1, 1, 1), |
| + string_attributes=['name', 'type'], |
| + children=[ |
| + models.ChildType('owners', ownerType, True), |
| + models.ChildType('summary', summaryType, False), |
| + ]) |
| + |
| +rapporMetricsType = models.ObjectNodeType('rappor-metrics', |
| + extra_newlines=(1, 1, 1), |
| + dont_indent=True, |
| + children=[ |
| + models.ChildType('metrics', rapporMetricType, True), |
| + ]) |
| + |
| +rapporConfigurationType = models.ObjectNodeType('rappor-configuration', |
| + dont_indent=True, |
| + children=[ |
| + models.ChildType('parameterTypes', rapporParameterTypesType, False), |
| + models.ChildType('metrics', rapporMetricsType, False), |
| + ]) |
| + |
| +rapporXmlType = models.DocumentType(rapporConfigurationType) |
| + |
| + |
| +def HasMissingOwners(metrics): |
|
Alexei Svitkine (slow)
2015/02/13 14:36:37
Can you add some tests?
Steven Holte
2015/02/14 01:11:18
Done.
|
| + """Check that all of the metrics have owners. |
| + |
| + Args: |
| + metrics: A list of rappor metric description objects. |
| + |
| + Returns: |
| + True iff some metrics are missing owners. |
| + """ |
| + missing_owners = [m for m in metrics if not m['owners']] |
| + for metric in missing_owners: |
| + logging.error('Rappor metric "%s" is missing an owner.', metric['name']) |
| + print metric |
| + return bool(missing_owners) |
| + |
| + |
| +def HasInvalidTypes(type_names, metrics): |
| + """Check that all of the metrics have valid types. |
| + |
| + Args: |
| + type_names: The set of valid type names. |
| + metrics: A list of rappor metric description objects. |
| + |
| + Returns: |
| + True iff some metrics have invalid types. |
| + """ |
| + invalid_types = [m for m in metrics if m['type'] not in type_names] |
| + for metric in invalid_types: |
| + logging.error('Rappor metric "%s" has invalid type "%s"', |
| + metric['name'], metric['type']) |
| + return bool(invalid_types) |
| + |
| + |
| +def HasErrors(obj): |
| + """Check that rappor.xml passes some basic validation checks. |
| + |
| + Args: |
| + obj: The parsed rappor.xml contents. |
| + |
| + Returns: |
| + True iff there are validation errors. |
| + """ |
| + metrics = obj['metrics']['metrics'] |
| + type_names = set(p['name'] for p in obj['parameterTypes']['types']) |
| + return (HasMissingOwners(metrics) or |
| + HasInvalidTypes(type_names, metrics)) |
| + |
| + |
| +def Cleanup(obj): |
| + """Preform cleanup on description contents, such as sorting metrics. |
| + |
| + Args: |
| + obj: The parsed rappor.xml contents. |
| + """ |
| + types = obj['parameterTypes']['types'] |
| + types.sort(key=lambda x: x['name']) |
| + metrics = obj['metrics']['metrics'] |
| + metrics.sort(key=lambda x: x['name']) |
| + |
| + |
| +def UpdateXml(original_xml): |
| + """Parse the original xml and return a pretty printed version. |
| + |
| + Args: |
| + original_xml: A string containing the original xml file contents. |
| + |
| + Returns: |
| + A Pretty printed xml string. |
| + """ |
| + comments, obj = rapporXmlType.Parse(original_xml) |
| + |
| + if HasErrors(obj): |
| + return None |
| + |
| + Cleanup(obj) |
| + |
| + return rapporXmlType.PrettyPrint(comments, obj) |
| + |
| + |
| +def main(argv): |
| + presubmit_util.DoPresubmitMain(argv, 'rappor.xml', 'rappor.old.xml', |
| + 'pretty_print.py', UpdateXml) |
| + |
| + |
| +if '__main__' == __name__: |
| + sys.exit(main(sys.argv)) |