OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 # Copyright 2015 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 import logging | |
7 import sys | |
8 import os | |
9 | |
10 # Import the metrics/common module for pretty print xml. | |
11 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common')) | |
12 import models | |
13 import presubmit_util | |
14 | |
15 | |
16 # Model definitions for rappor.xml content | |
17 _SUMMARY_TYPE = models.TextNodeType('summary') | |
18 | |
19 _PARAMETERS_TYPE = models.ObjectNodeType('parameters', | |
20 int_attributes=[ | |
21 'num-cohorts', | |
22 'bytes', | |
23 'hash-functions', | |
24 ], | |
25 float_attributes=[ | |
26 'fake-prob', | |
27 'fake-one-prob', | |
28 'one-coin-prob', | |
29 'zero-coin-prob', | |
30 ], | |
31 string_attributes=[ | |
32 'reporting-level' | |
33 ]) | |
34 | |
35 _RAPPOR_PARAMETERS_TYPE = models.ObjectNodeType('rappor-parameters', | |
36 extra_newlines=(1, 1, 1), | |
37 string_attributes=['name'], | |
38 children=[ | |
39 models.ChildType('summary', _SUMMARY_TYPE, False), | |
40 models.ChildType('parameters', _PARAMETERS_TYPE, False), | |
41 ]) | |
42 | |
43 _RAPPOR_PARAMETERS_TYPES_TYPE = models.ObjectNodeType('rappor-parameter-types', | |
44 extra_newlines=(1, 1, 1), | |
45 dont_indent=True, | |
46 children=[ | |
47 models.ChildType('types', _RAPPOR_PARAMETERS_TYPE, True), | |
48 ]) | |
49 | |
50 _OWNER_TYPE = models.TextNodeType('owner', single_line=True) | |
51 | |
52 _RAPPOR_METRIC_TYPE = models.ObjectNodeType('rappor-metric', | |
53 extra_newlines=(1, 1, 1), | |
54 string_attributes=['name', 'type'], | |
55 children=[ | |
56 models.ChildType('owners', _OWNER_TYPE, True), | |
57 models.ChildType('summary', _SUMMARY_TYPE, False), | |
58 ]) | |
59 | |
60 _RAPPOR_METRICS_TYPE = models.ObjectNodeType('rappor-metrics', | |
61 extra_newlines=(1, 1, 1), | |
62 dont_indent=True, | |
63 children=[ | |
64 models.ChildType('metrics', _RAPPOR_METRIC_TYPE, True), | |
65 ]) | |
66 | |
67 _RAPPOR_CONFIGURATION_TYPE = models.ObjectNodeType('rappor-configuration', | |
68 extra_newlines=(1, 1, 1), | |
69 dont_indent=True, | |
70 children=[ | |
71 models.ChildType('parameterTypes', _RAPPOR_PARAMETERS_TYPES_TYPE, False), | |
72 models.ChildType('metrics', _RAPPOR_METRICS_TYPE, False), | |
73 ]) | |
74 | |
75 RAPPOR_XML_TYPE = models.DocumentType(_RAPPOR_CONFIGURATION_TYPE) | |
76 | |
77 | |
78 def GetTypeNames(config): | |
79 return set(p['name'] for p in config['parameterTypes']['types']) | |
80 | |
81 | |
82 def HasMissingOwners(metrics): | |
83 """Check that all of the metrics have owners. | |
84 | |
85 Args: | |
86 metrics: A list of rappor metric description objects. | |
87 | |
88 Returns: | |
89 True iff some metrics are missing owners. | |
90 """ | |
91 missing_owners = [m for m in metrics if not m['owners']] | |
92 for metric in missing_owners: | |
93 logging.error('Rappor metric "%s" is missing an owner.', metric['name']) | |
94 print metric | |
95 return bool(missing_owners) | |
96 | |
97 | |
98 def HasInvalidTypes(type_names, metrics): | |
99 """Check that all of the metrics have valid types. | |
100 | |
101 Args: | |
102 type_names: The set of valid type names. | |
103 metrics: A list of rappor metric description objects. | |
104 | |
105 Returns: | |
106 True iff some metrics have invalid types. | |
107 """ | |
108 invalid_types = [m for m in metrics if m['type'] not in type_names] | |
109 for metric in invalid_types: | |
110 logging.error('Rappor metric "%s" has invalid type "%s"', | |
111 metric['name'], metric['type']) | |
112 return bool(invalid_types) | |
113 | |
114 | |
115 def HasErrors(config): | |
116 """Check that rappor.xml passes some basic validation checks. | |
117 | |
118 Args: | |
119 config: The parsed rappor.xml contents. | |
120 | |
121 Returns: | |
122 True iff there are validation errors. | |
123 """ | |
124 metrics = config['metrics']['metrics'] | |
125 type_names = GetTypeNames(config) | |
126 return (HasMissingOwners(metrics) or | |
127 HasInvalidTypes(type_names, metrics)) | |
128 | |
129 | |
130 def Cleanup(config): | |
131 """Preform cleanup on description contents, such as sorting metrics. | |
132 | |
133 Args: | |
134 config: The parsed rappor.xml contents. | |
135 """ | |
136 types = config['parameterTypes']['types'] | |
137 types.sort(key=lambda x: x['name']) | |
138 metrics = config['metrics']['metrics'] | |
139 metrics.sort(key=lambda x: x['name']) | |
140 | |
141 | |
142 def UpdateXML(original_xml): | |
143 """Parse the original xml and return a pretty printed version. | |
144 | |
145 Args: | |
146 original_xml: A string containing the original xml file contents. | |
147 | |
148 Returns: | |
149 A Pretty printed xml string. | |
150 """ | |
151 comments, config = RAPPOR_XML_TYPE.Parse(original_xml) | |
152 | |
153 if HasErrors(config): | |
154 return None | |
155 | |
156 Cleanup(config) | |
157 | |
158 return RAPPOR_XML_TYPE.PrettyPrint(comments, config) | |
159 | |
160 | |
161 def main(argv): | |
162 presubmit_util.DoPresubmitMain(argv, 'rappor.xml', 'rappor.old.xml', | |
163 'pretty_print.py', UpdateXML) | |
164 | |
165 | |
166 if '__main__' == __name__: | |
167 sys.exit(main(sys.argv)) | |
OLD | NEW |