OLD | NEW |
---|---|
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 | 2 |
3 """ | 3 """ |
4 Copyright 2014 Google Inc. | 4 Copyright 2014 Google Inc. |
5 | 5 |
6 Use of this source code is governed by a BSD-style license that can be | 6 Use of this source code is governed by a BSD-style license that can be |
7 found in the LICENSE file. | 7 found in the LICENSE file. |
8 | 8 |
9 ImagePairSet class; see its docstring below. | 9 ImagePairSet class; see its docstring below. |
10 """ | 10 """ |
11 | 11 |
12 # System-level imports | 12 # System-level imports |
13 import posixpath | 13 import posixpath |
14 | 14 |
15 # Local imports | 15 # Must fix up PYTHONPATH before importing from within Skia |
16 import fix_pythonpath # pylint: disable=W0611 | |
17 | |
18 # Imports from within Skia | |
16 import column | 19 import column |
17 import imagediffdb | 20 import imagediffdb |
21 from py.utils import gs_utils | |
18 | 22 |
19 # Keys used within dictionary representation of ImagePairSet. | 23 # Keys used within dictionary representation of ImagePairSet. |
20 # NOTE: Keep these in sync with static/constants.js | 24 # NOTE: Keep these in sync with static/constants.js |
21 KEY__ROOT__EXTRACOLUMNHEADERS = 'extraColumnHeaders' | 25 KEY__ROOT__EXTRACOLUMNHEADERS = 'extraColumnHeaders' |
22 KEY__ROOT__EXTRACOLUMNORDER = 'extraColumnOrder' | 26 KEY__ROOT__EXTRACOLUMNORDER = 'extraColumnOrder' |
23 KEY__ROOT__HEADER = 'header' | 27 KEY__ROOT__HEADER = 'header' |
24 KEY__ROOT__IMAGEPAIRS = 'imagePairs' | 28 KEY__ROOT__IMAGEPAIRS = 'imagePairs' |
25 KEY__ROOT__IMAGESETS = 'imageSets' | 29 KEY__ROOT__IMAGESETS = 'imageSets' |
26 KEY__IMAGESETS__FIELD__BASE_URL = 'baseUrl' | 30 KEY__IMAGESETS__FIELD__BASE_URL = 'baseUrl' |
27 KEY__IMAGESETS__FIELD__DESCRIPTION = 'description' | 31 KEY__IMAGESETS__FIELD__DESCRIPTION = 'description' |
(...skipping 18 matching lines...) Expand all Loading... | |
46 """ | 50 """ |
47 Args: | 51 Args: |
48 diff_base_url: base URL indicating where diff images can be loaded from | 52 diff_base_url: base URL indicating where diff images can be loaded from |
49 descriptions: a (string, string) tuple describing the two image sets. | 53 descriptions: a (string, string) tuple describing the two image sets. |
50 If not specified, DEFAULT_DESCRIPTIONS will be used. | 54 If not specified, DEFAULT_DESCRIPTIONS will be used. |
51 """ | 55 """ |
52 self._column_header_factories = {} | 56 self._column_header_factories = {} |
53 self._descriptions = descriptions or DEFAULT_DESCRIPTIONS | 57 self._descriptions = descriptions or DEFAULT_DESCRIPTIONS |
54 self._extra_column_tallies = {} # maps column_id -> values | 58 self._extra_column_tallies = {} # maps column_id -> values |
55 # -> instances_per_value | 59 # -> instances_per_value |
56 self._image_pair_dicts = [] | |
57 self._image_base_url = None | 60 self._image_base_url = None |
58 self._diff_base_url = diff_base_url | 61 self._diff_base_url = diff_base_url |
59 | 62 |
63 # We build self._image_pair_objects incrementally as calls come into | |
64 # add_image_pair(); self._image_pair_dicts is filled in lazily (so that | |
65 # we put off asking ImageDiffDB for results as long as possible). | |
66 self._image_pair_objects = [] | |
67 self._image_pair_dicts = None | |
68 | |
60 def add_image_pair(self, image_pair): | 69 def add_image_pair(self, image_pair): |
61 """Adds an ImagePair; this may be repeated any number of times.""" | 70 """Adds an ImagePair; this may be repeated any number of times.""" |
62 # Special handling when we add the first ImagePair... | 71 # Special handling when we add the first ImagePair... |
63 if not self._image_pair_dicts: | 72 if not self._image_pair_objects: |
64 self._image_base_url = image_pair.base_url | 73 self._image_base_url = image_pair.base_url |
65 | 74 |
66 if image_pair.base_url != self._image_base_url: | 75 if image_pair.base_url != self._image_base_url: |
67 raise Exception('added ImagePair with base_url "%s" instead of "%s"' % ( | 76 raise Exception('added ImagePair with base_url "%s" instead of "%s"' % ( |
68 image_pair.base_url, self._image_base_url)) | 77 image_pair.base_url, self._image_base_url)) |
69 self._image_pair_dicts.append(image_pair.as_dict()) | 78 self._image_pair_objects.append(image_pair) |
70 extra_columns_dict = image_pair.extra_columns_dict | 79 extra_columns_dict = image_pair.extra_columns_dict |
71 if extra_columns_dict: | 80 if extra_columns_dict: |
72 for column_id, value in extra_columns_dict.iteritems(): | 81 for column_id, value in extra_columns_dict.iteritems(): |
73 self._add_extra_column_value_to_summary(column_id, value) | 82 self._add_extra_column_value_to_summary(column_id, value) |
74 | 83 |
75 def set_column_header_factory(self, column_id, column_header_factory): | 84 def set_column_header_factory(self, column_id, column_header_factory): |
76 """Overrides the default settings for one of the extraColumn headers. | 85 """Overrides the default settings for one of the extraColumn headers. |
77 | 86 |
78 Args: | 87 Args: |
79 column_id: string; unique ID of this column (must match a key within | 88 column_id: string; unique ID of this column (must match a key within |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
134 column_header_factory = self.get_column_header_factory(column_id) | 143 column_header_factory = self.get_column_header_factory(column_id) |
135 asdict[column_id] = column_header_factory.create_as_dict( | 144 asdict[column_id] = column_header_factory.create_as_dict( |
136 values_for_column) | 145 values_for_column) |
137 return asdict | 146 return asdict |
138 | 147 |
139 def as_dict(self, column_ids_in_order=None): | 148 def as_dict(self, column_ids_in_order=None): |
140 """Returns a dictionary describing this package of ImagePairs. | 149 """Returns a dictionary describing this package of ImagePairs. |
141 | 150 |
142 Uses the KEY__* constants as keys. | 151 Uses the KEY__* constants as keys. |
143 | 152 |
144 Params: | 153 Args: |
145 column_ids_in_order: A list of all extracolumn IDs in the desired display | 154 column_ids_in_order: A list of all extracolumn IDs in the desired display |
146 order. If unspecified, they will be displayed in alphabetical order. | 155 order. If unspecified, they will be displayed in alphabetical order. |
147 If specified, this list must contain all the extracolumn IDs! | 156 If specified, this list must contain all the extracolumn IDs! |
148 (It may contain extra column IDs; they will be ignored.) | 157 (It may contain extra column IDs; they will be ignored.) |
149 """ | 158 """ |
150 all_column_ids = set(self._extra_column_tallies.keys()) | 159 all_column_ids = set(self._extra_column_tallies.keys()) |
151 if column_ids_in_order == None: | 160 if column_ids_in_order == None: |
152 column_ids_in_order = sorted(all_column_ids) | 161 column_ids_in_order = sorted(all_column_ids) |
153 else: | 162 else: |
154 # Make sure the caller listed all column IDs, and throw away any extras. | 163 # Make sure the caller listed all column IDs, and throw away any extras. |
155 specified_column_ids = set(column_ids_in_order) | 164 specified_column_ids = set(column_ids_in_order) |
156 forgotten_column_ids = all_column_ids - specified_column_ids | 165 forgotten_column_ids = all_column_ids - specified_column_ids |
157 assert not forgotten_column_ids, ( | 166 assert not forgotten_column_ids, ( |
158 'column_ids_in_order %s missing these column_ids: %s' % ( | 167 'column_ids_in_order %s missing these column_ids: %s' % ( |
159 column_ids_in_order, forgotten_column_ids)) | 168 column_ids_in_order, forgotten_column_ids)) |
160 column_ids_in_order = [c for c in column_ids_in_order | 169 column_ids_in_order = [c for c in column_ids_in_order |
161 if c in all_column_ids] | 170 if c in all_column_ids] |
162 | 171 |
163 key_description = KEY__IMAGESETS__FIELD__DESCRIPTION | 172 key_description = KEY__IMAGESETS__FIELD__DESCRIPTION |
164 key_base_url = KEY__IMAGESETS__FIELD__BASE_URL | 173 key_base_url = KEY__IMAGESETS__FIELD__BASE_URL |
174 if gs_utils.GSUtils.is_gs_url(self._image_base_url): | |
175 value_base_url = self._convert_gs_url_to_http_url(self._image_base_url) | |
176 else: | |
177 value_base_url = self._image_base_url | |
178 | |
179 # We've waited as long as we can to ask ImageDiffDB for details of the | |
180 # image diffs, so that it has time to compute them. | |
181 if self._image_pair_dicts == None: | |
182 self._image_pair_dicts = [ip.as_dict() for ip in self._image_pair_objects] | |
183 | |
165 return { | 184 return { |
166 KEY__ROOT__EXTRACOLUMNHEADERS: self._column_headers_as_dict(), | 185 KEY__ROOT__EXTRACOLUMNHEADERS: self._column_headers_as_dict(), |
167 KEY__ROOT__EXTRACOLUMNORDER: column_ids_in_order, | 186 KEY__ROOT__EXTRACOLUMNORDER: column_ids_in_order, |
168 KEY__ROOT__IMAGEPAIRS: self._image_pair_dicts, | 187 KEY__ROOT__IMAGEPAIRS: self._image_pair_dicts, |
169 KEY__ROOT__IMAGESETS: { | 188 KEY__ROOT__IMAGESETS: { |
170 KEY__IMAGESETS__SET__IMAGE_A: { | 189 KEY__IMAGESETS__SET__IMAGE_A: { |
171 key_description: self._descriptions[0], | 190 key_description: self._descriptions[0], |
172 key_base_url: self._image_base_url, | 191 key_base_url: value_base_url, |
173 }, | 192 }, |
174 KEY__IMAGESETS__SET__IMAGE_B: { | 193 KEY__IMAGESETS__SET__IMAGE_B: { |
175 key_description: self._descriptions[1], | 194 key_description: self._descriptions[1], |
176 key_base_url: self._image_base_url, | 195 key_base_url: value_base_url, |
177 }, | 196 }, |
178 KEY__IMAGESETS__SET__DIFFS: { | 197 KEY__IMAGESETS__SET__DIFFS: { |
179 key_description: 'color difference per channel', | 198 key_description: 'color difference per channel', |
180 key_base_url: posixpath.join( | 199 key_base_url: posixpath.join( |
181 self._diff_base_url, imagediffdb.RGBDIFFS_SUBDIR), | 200 self._diff_base_url, imagediffdb.RGBDIFFS_SUBDIR), |
182 }, | 201 }, |
183 KEY__IMAGESETS__SET__WHITEDIFFS: { | 202 KEY__IMAGESETS__SET__WHITEDIFFS: { |
184 key_description: 'differing pixels in white', | 203 key_description: 'differing pixels in white', |
185 key_base_url: posixpath.join( | 204 key_base_url: posixpath.join( |
186 self._diff_base_url, imagediffdb.WHITEDIFFS_SUBDIR), | 205 self._diff_base_url, imagediffdb.WHITEDIFFS_SUBDIR), |
187 }, | 206 }, |
188 }, | 207 }, |
189 } | 208 } |
209 | |
210 @staticmethod | |
211 def _convert_gs_url_to_http_url(gs_url): | |
212 """Returns HTTP URL that can be used to download this Google Storage file. | |
213 | |
214 TODO(epoger): Create functionality like this within gs_utils.py instead of | |
215 here? See https://codereview.chromium.org/428493005/ ('create | |
216 anyfile_utils.py for copying files between HTTP/GS/local filesystem') | |
217 | |
218 Args: | |
219 gs_url: "gs://bucket/path" format URL | |
220 """ | |
221 (bucket, path) = gs_utils.GSUtils.split_gs_url(gs_url) | |
rmistry
2014/08/04 20:52:49
Can directly do:
bucket, path = gs_utils.GSUtils..
epoger
2014/08/05 03:34:08
Done.
| |
222 http_url = 'http://%s.commondatastorage.googleapis.com' % bucket | |
223 if path: | |
224 http_url += '/' + path | |
225 return http_url | |
OLD | NEW |