blob: 48c30647183e27736b44f46636b63231bd0beaa9 [file] [log] [blame]
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +00001#!/usr/bin/python
2
3"""
4Copyright 2014 Google Inc.
5
6Use of this source code is governed by a BSD-style license that can be
7found in the LICENSE file.
8
9ImagePairSet class; see its docstring below.
10"""
11
commit-bot@chromium.orgd1c85d22014-03-17 14:22:02 +000012# System-level imports
13import posixpath
14
15# Local imports
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +000016import column
17
18# Keys used within dictionary representation of ImagePairSet.
commit-bot@chromium.org16f41802014-02-26 19:05:20 +000019# NOTE: Keep these in sync with static/constants.js
20KEY__EXTRACOLUMNHEADERS = 'extraColumnHeaders'
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +000021KEY__IMAGEPAIRS = 'imagePairs'
22KEY__IMAGESETS = 'imageSets'
commit-bot@chromium.orgd1c85d22014-03-17 14:22:02 +000023KEY__IMAGESETS__FIELD__BASE_URL = 'baseUrl'
24KEY__IMAGESETS__FIELD__DESCRIPTION = 'description'
25KEY__IMAGESETS__SET__DIFFS = 'diffs'
26KEY__IMAGESETS__SET__IMAGE_A = 'imageA'
27KEY__IMAGESETS__SET__IMAGE_B = 'imageB'
28KEY__IMAGESETS__SET__WHITEDIFFS = 'whiteDiffs'
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +000029
30DEFAULT_DESCRIPTIONS = ('setA', 'setB')
commit-bot@chromium.orgd1c85d22014-03-17 14:22:02 +000031DIFF_BASE_URL = '/static/generated-images'
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +000032
33
34class 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.orgd1c85d22014-03-17 14:22:02 +000054 self._image_base_url = None
55 self._diff_base_url = DIFF_BASE_URL
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +000056
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.orgd1c85d22014-03-17 14:22:02 +000061 self._image_base_url = image_pair.base_url
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +000062
commit-bot@chromium.orgd1c85d22014-03-17 14:22:02 +000063 if image_pair.base_url != self._image_base_url:
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +000064 raise Exception('added ImagePair with base_url "%s" instead of "%s"' % (
commit-bot@chromium.orgd1c85d22014-03-17 14:22:02 +000065 image_pair.base_url, self._image_base_url))
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +000066 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.org16f41802014-02-26 19:05:20 +000070 self._add_extra_column_value_to_summary(column_id, value)
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +000071
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.org16f41802014-02-26 19:05:20 +000095 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.org536b15f2014-02-13 17:17:05 +0000107 """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.org16f41802014-02-26 19:05:20 +0000112
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.org536b15f2014-02-13 17:17:05 +0000118 """
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.org16f41802014-02-26 19:05:20 +0000124 instances_of_this_value += addend
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +0000125 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.orgd1c85d22014-03-17 14:22:02 +0000141 key_description = KEY__IMAGESETS__FIELD__DESCRIPTION
142 key_base_url = KEY__IMAGESETS__FIELD__BASE_URL
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +0000143 return {
commit-bot@chromium.org16f41802014-02-26 19:05:20 +0000144 KEY__EXTRACOLUMNHEADERS: self._column_headers_as_dict(),
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +0000145 KEY__IMAGEPAIRS: self._image_pair_dicts,
commit-bot@chromium.orgd1c85d22014-03-17 14:22:02 +0000146 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.org536b15f2014-02-13 17:17:05 +0000166 }