commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 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 | |
commit-bot@chromium.org | d1c85d2 | 2014-03-17 14:22:02 +0000 | [diff] [blame^] | 12 | # System-level imports |
| 13 | import posixpath |
| 14 | |
| 15 | # Local imports |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 16 | import column |
| 17 | |
| 18 | # Keys used within dictionary representation of ImagePairSet. |
commit-bot@chromium.org | 16f4180 | 2014-02-26 19:05:20 +0000 | [diff] [blame] | 19 | # NOTE: Keep these in sync with static/constants.js |
| 20 | KEY__EXTRACOLUMNHEADERS = 'extraColumnHeaders' |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 21 | KEY__IMAGEPAIRS = 'imagePairs' |
| 22 | KEY__IMAGESETS = 'imageSets' |
commit-bot@chromium.org | d1c85d2 | 2014-03-17 14:22:02 +0000 | [diff] [blame^] | 23 | KEY__IMAGESETS__FIELD__BASE_URL = 'baseUrl' |
| 24 | KEY__IMAGESETS__FIELD__DESCRIPTION = 'description' |
| 25 | KEY__IMAGESETS__SET__DIFFS = 'diffs' |
| 26 | KEY__IMAGESETS__SET__IMAGE_A = 'imageA' |
| 27 | KEY__IMAGESETS__SET__IMAGE_B = 'imageB' |
| 28 | KEY__IMAGESETS__SET__WHITEDIFFS = 'whiteDiffs' |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 29 | |
| 30 | DEFAULT_DESCRIPTIONS = ('setA', 'setB') |
commit-bot@chromium.org | d1c85d2 | 2014-03-17 14:22:02 +0000 | [diff] [blame^] | 31 | DIFF_BASE_URL = '/static/generated-images' |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 32 | |
| 33 | |
| 34 | class ImagePairSet(object): |
| 35 | """A collection of ImagePairs, representing two arbitrary sets of images. |
| 36 | |
| 37 | These could be: |
| 38 | - images generated before and after a code patch |
| 39 | - expected and actual images for some tests |
| 40 | - or any other pairwise set of images. |
| 41 | """ |
| 42 | |
| 43 | def __init__(self, descriptions=None): |
| 44 | """ |
| 45 | Args: |
| 46 | descriptions: a (string, string) tuple describing the two image sets. |
| 47 | If not specified, DEFAULT_DESCRIPTIONS will be used. |
| 48 | """ |
| 49 | self._column_header_factories = {} |
| 50 | self._descriptions = descriptions or DEFAULT_DESCRIPTIONS |
| 51 | self._extra_column_tallies = {} # maps column_id -> values |
| 52 | # -> instances_per_value |
| 53 | self._image_pair_dicts = [] |
commit-bot@chromium.org | d1c85d2 | 2014-03-17 14:22:02 +0000 | [diff] [blame^] | 54 | self._image_base_url = None |
| 55 | self._diff_base_url = DIFF_BASE_URL |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 56 | |
| 57 | def add_image_pair(self, image_pair): |
| 58 | """Adds an ImagePair; this may be repeated any number of times.""" |
| 59 | # Special handling when we add the first ImagePair... |
| 60 | if not self._image_pair_dicts: |
commit-bot@chromium.org | d1c85d2 | 2014-03-17 14:22:02 +0000 | [diff] [blame^] | 61 | self._image_base_url = image_pair.base_url |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 62 | |
commit-bot@chromium.org | d1c85d2 | 2014-03-17 14:22:02 +0000 | [diff] [blame^] | 63 | if image_pair.base_url != self._image_base_url: |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 64 | raise Exception('added ImagePair with base_url "%s" instead of "%s"' % ( |
commit-bot@chromium.org | d1c85d2 | 2014-03-17 14:22:02 +0000 | [diff] [blame^] | 65 | image_pair.base_url, self._image_base_url)) |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 66 | self._image_pair_dicts.append(image_pair.as_dict()) |
| 67 | extra_columns_dict = image_pair.extra_columns_dict |
| 68 | if extra_columns_dict: |
| 69 | for column_id, value in extra_columns_dict.iteritems(): |
commit-bot@chromium.org | 16f4180 | 2014-02-26 19:05:20 +0000 | [diff] [blame] | 70 | self._add_extra_column_value_to_summary(column_id, value) |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 71 | |
| 72 | def set_column_header_factory(self, column_id, column_header_factory): |
| 73 | """Overrides the default settings for one of the extraColumn headers. |
| 74 | |
| 75 | Args: |
| 76 | column_id: string; unique ID of this column (must match a key within |
| 77 | an ImagePair's extra_columns dictionary) |
| 78 | column_header_factory: a ColumnHeaderFactory object |
| 79 | """ |
| 80 | self._column_header_factories[column_id] = column_header_factory |
| 81 | |
| 82 | def get_column_header_factory(self, column_id): |
| 83 | """Returns the ColumnHeaderFactory object for a particular extraColumn. |
| 84 | |
| 85 | Args: |
| 86 | column_id: string; unique ID of this column (must match a key within |
| 87 | an ImagePair's extra_columns dictionary) |
| 88 | """ |
| 89 | column_header_factory = self._column_header_factories.get(column_id, None) |
| 90 | if not column_header_factory: |
| 91 | column_header_factory = column.ColumnHeaderFactory(header_text=column_id) |
| 92 | self._column_header_factories[column_id] = column_header_factory |
| 93 | return column_header_factory |
| 94 | |
commit-bot@chromium.org | 16f4180 | 2014-02-26 19:05:20 +0000 | [diff] [blame] | 95 | def ensure_extra_column_values_in_summary(self, column_id, values): |
| 96 | """Ensure this column_id/value pair is part of the extraColumns summary. |
| 97 | |
| 98 | Args: |
| 99 | column_id: string; unique ID of this column |
| 100 | value: string; a possible value for this column |
| 101 | """ |
| 102 | for value in values: |
| 103 | self._add_extra_column_value_to_summary( |
| 104 | column_id=column_id, value=value, addend=0) |
| 105 | |
| 106 | def _add_extra_column_value_to_summary(self, column_id, value, addend=1): |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 107 | """Records one column_id/value extraColumns pair found within an ImagePair. |
| 108 | |
| 109 | We use this information to generate tallies within the column header |
| 110 | (how many instances we saw of a particular value, within a particular |
| 111 | extraColumn). |
commit-bot@chromium.org | 16f4180 | 2014-02-26 19:05:20 +0000 | [diff] [blame] | 112 | |
| 113 | Args: |
| 114 | column_id: string; unique ID of this column (must match a key within |
| 115 | an ImagePair's extra_columns dictionary) |
| 116 | value: string; a possible value for this column |
| 117 | addend: integer; how many instances to add to the tally |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 118 | """ |
| 119 | known_values_for_column = self._extra_column_tallies.get(column_id, None) |
| 120 | if not known_values_for_column: |
| 121 | known_values_for_column = {} |
| 122 | self._extra_column_tallies[column_id] = known_values_for_column |
| 123 | instances_of_this_value = known_values_for_column.get(value, 0) |
commit-bot@chromium.org | 16f4180 | 2014-02-26 19:05:20 +0000 | [diff] [blame] | 124 | instances_of_this_value += addend |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 125 | known_values_for_column[value] = instances_of_this_value |
| 126 | |
| 127 | def _column_headers_as_dict(self): |
| 128 | """Returns all column headers as a dictionary.""" |
| 129 | asdict = {} |
| 130 | for column_id, values_for_column in self._extra_column_tallies.iteritems(): |
| 131 | column_header_factory = self.get_column_header_factory(column_id) |
| 132 | asdict[column_id] = column_header_factory.create_as_dict( |
| 133 | values_for_column) |
| 134 | return asdict |
| 135 | |
| 136 | def as_dict(self): |
| 137 | """Returns a dictionary describing this package of ImagePairs. |
| 138 | |
| 139 | Uses the KEY__* constants as keys. |
| 140 | """ |
commit-bot@chromium.org | d1c85d2 | 2014-03-17 14:22:02 +0000 | [diff] [blame^] | 141 | key_description = KEY__IMAGESETS__FIELD__DESCRIPTION |
| 142 | key_base_url = KEY__IMAGESETS__FIELD__BASE_URL |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 143 | return { |
commit-bot@chromium.org | 16f4180 | 2014-02-26 19:05:20 +0000 | [diff] [blame] | 144 | KEY__EXTRACOLUMNHEADERS: self._column_headers_as_dict(), |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 145 | KEY__IMAGEPAIRS: self._image_pair_dicts, |
commit-bot@chromium.org | d1c85d2 | 2014-03-17 14:22:02 +0000 | [diff] [blame^] | 146 | KEY__IMAGESETS: { |
| 147 | KEY__IMAGESETS__SET__IMAGE_A: { |
| 148 | key_description: self._descriptions[0], |
| 149 | key_base_url: self._image_base_url, |
| 150 | }, |
| 151 | KEY__IMAGESETS__SET__IMAGE_B: { |
| 152 | key_description: self._descriptions[1], |
| 153 | key_base_url: self._image_base_url, |
| 154 | }, |
| 155 | KEY__IMAGESETS__SET__DIFFS: { |
| 156 | key_description: 'color difference per channel', |
| 157 | key_base_url: posixpath.join( |
| 158 | self._diff_base_url, 'diffs'), |
| 159 | }, |
| 160 | KEY__IMAGESETS__SET__WHITEDIFFS: { |
| 161 | key_description: 'differing pixels in white', |
| 162 | key_base_url: posixpath.join( |
| 163 | self._diff_base_url, 'whitediffs'), |
| 164 | }, |
| 165 | }, |
commit-bot@chromium.org | 536b15f | 2014-02-13 17:17:05 +0000 | [diff] [blame] | 166 | } |