OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/python | |
2 | |
3 """ | |
4 Copyright 2014 Google Inc. | |
5 | |
6 Use of this source code is governed by a BSD-style license that can be | |
7 found in the LICENSE file. | |
8 | |
9 ImagePairSet class; see its docstring below. | |
10 """ | |
11 | |
12 import column | |
13 | |
14 # Keys used within dictionary representation of ImagePairSet. | |
15 KEY__COLUMNHEADERS = 'columnHeaders' | |
16 KEY__IMAGEPAIRS = 'imagePairs' | |
17 KEY__IMAGESETS = 'imageSets' | |
18 KEY__IMAGESETS__BASE_URL = 'baseUrl' | |
19 KEY__IMAGESETS__DESCRIPTION = 'description' | |
20 | |
21 | |
22 class ImagePairSet(object): | |
23 """ A collection of ImagePairs, representing two arbitrary sets of images we | |
rmistry
2014/02/12 19:23:56
Nit: Single line description please.
epoger
2014/02/12 19:59:16
Done.
| |
24 want to compare. | |
25 | |
26 These could be: | |
27 - images generated before and after a code patch | |
28 - expected and actual images for some tests | |
29 - or any other pairwise set of images. | |
30 """ | |
31 | |
32 def __init__(self, descriptions=None): | |
33 """ | |
34 Args: | |
35 descriptions: a (string, string) tuple describing the two image sets | |
rmistry
2014/02/12 19:23:56
Should we mention here what is used if description
epoger
2014/02/12 19:59:16
Done.
epoger
2014/02/12 19:59:16
Done.
| |
36 """ | |
37 self._column_header_factories = {} | |
38 self._descriptions = descriptions or ('setA', 'setB') | |
39 self._extra_column_tallies = {} # maps column_id -> values | |
40 # -> instances_per_value | |
41 self._image_pair_dicts = [] | |
42 | |
43 def add_image_pair(self, image_pair): | |
44 """Add an ImagePair; this may be repeated any number of times.""" | |
45 # Special handling when we add the first ImagePair... | |
46 if not self._image_pair_dicts: | |
47 self._base_url = image_pair.base_url | |
48 | |
49 if image_pair.base_url != self._base_url: | |
50 raise Exception('added ImagePair with base_url "%s" instead of "%s"' % ( | |
51 image_pair.base_url, self._base_url)) | |
52 self._image_pair_dicts.append(image_pair.as_dict()) | |
53 extra_columns_dict = image_pair.extra_columns_dict | |
54 if extra_columns_dict: | |
55 for column_id, value in extra_columns_dict.iteritems(): | |
56 self._add_extra_column_entry(column_id, value) | |
57 | |
58 def set_column_header_factory(self, column_id, column_header_factory): | |
59 """Override the default settings for one of the extraColumn headers. | |
60 | |
61 Args: | |
62 column_id: string; unique ID of this column (must match a key within | |
63 an ImagePair's extra_columns dictionary) | |
64 column_header_factory: a ColumnHeaderFactory object | |
65 """ | |
66 self._column_header_factories[column_id] = column_header_factory | |
67 | |
68 def get_column_header_factory(self, column_id): | |
69 """Returns the appropriate ColumnHeaderFactory object for assembling a | |
70 header for a particular extraColumn. | |
rmistry
2014/02/12 19:23:56
Nit: Single line please
epoger
2014/02/12 19:59:16
Done.
| |
71 | |
72 Args: | |
73 column_id: string; unique ID of this column (must match a key within | |
74 an ImagePair's extra_columns dictionary) | |
75 """ | |
76 try: | |
77 return self._column_header_factories[column_id] | |
rmistry
2014/02/12 19:23:56
How about something like this instead:
if not sel
epoger
2014/02/12 19:59:16
I can do that if you like, but I figured the exist
rmistry
2014/02/13 12:10:02
No I do not think there is significant performance
| |
78 except KeyError: | |
79 self._column_header_factories[column_id] = column.ColumnHeaderFactory( | |
80 header_text=column_id) | |
81 return self._column_header_factories[column_id] | |
82 | |
83 def _add_extra_column_entry(self, column_id, value): | |
84 """Record one column_id/value extraColumns pair found within an ImagePair. | |
85 We use this information to generate tallies within the column header | |
rmistry
2014/02/12 19:23:56
Nit: newline between the single line description a
epoger
2014/02/12 19:59:16
Done.
| |
86 (how many instances we saw of a particular value, within a particular | |
87 extraColumn). | |
88 """ | |
89 try: | |
90 known_values_for_column = self._extra_column_tallies[column_id] | |
91 except KeyError: | |
92 known_values_for_column = {} | |
93 self._extra_column_tallies[column_id] = known_values_for_column | |
94 instances_of_this_value = known_values_for_column.get(value, 0) | |
95 instances_of_this_value += 1 | |
96 known_values_for_column[value] = instances_of_this_value | |
97 | |
98 def _column_headers_as_dict(self): | |
99 """ | |
100 Return all column headers as a dictionary, suitable for returning | |
101 within the ImagePairSet dictionary representation. | |
102 """ | |
103 asdict = {} | |
104 for column_id, values_for_column in self._extra_column_tallies.iteritems(): | |
105 column_header_factory = self.get_column_header_factory(column_id) | |
106 asdict[column_id] = column_header_factory.create_as_dict( | |
107 values_for_column) | |
108 return asdict | |
109 | |
110 def as_dict(self): | |
111 """ | |
112 Return a dictionary describing this package of ImagePairs, as needed when | |
113 constructing the JSON representation. Uses the KEY__* constants as keys. | |
114 """ | |
115 return { | |
116 KEY__COLUMNHEADERS: self._column_headers_as_dict(), | |
117 KEY__IMAGEPAIRS: self._image_pair_dicts, | |
118 KEY__IMAGESETS: [{ | |
119 KEY__IMAGESETS__BASE_URL: self._base_url, | |
120 KEY__IMAGESETS__DESCRIPTION: self._descriptions[0], | |
121 }, { | |
122 KEY__IMAGESETS__BASE_URL: self._base_url, | |
123 KEY__IMAGESETS__DESCRIPTION: self._descriptions[1], | |
124 }], | |
125 } | |
OLD | NEW |