blob: 26c833e1b0bd95e1c9c39ebb3103307209d5507a [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
12import column
13
14# Keys used within dictionary representation of ImagePairSet.
commit-bot@chromium.org16f41802014-02-26 19:05:20 +000015# NOTE: Keep these in sync with static/constants.js
16KEY__EXTRACOLUMNHEADERS = 'extraColumnHeaders'
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +000017KEY__IMAGEPAIRS = 'imagePairs'
18KEY__IMAGESETS = 'imageSets'
19KEY__IMAGESETS__BASE_URL = 'baseUrl'
20KEY__IMAGESETS__DESCRIPTION = 'description'
21
22DEFAULT_DESCRIPTIONS = ('setA', 'setB')
23
24
25class ImagePairSet(object):
26 """A collection of ImagePairs, representing two arbitrary sets of images.
27
28 These could be:
29 - images generated before and after a code patch
30 - expected and actual images for some tests
31 - or any other pairwise set of images.
32 """
33
34 def __init__(self, descriptions=None):
35 """
36 Args:
37 descriptions: a (string, string) tuple describing the two image sets.
38 If not specified, DEFAULT_DESCRIPTIONS will be used.
39 """
40 self._column_header_factories = {}
41 self._descriptions = descriptions or DEFAULT_DESCRIPTIONS
42 self._extra_column_tallies = {} # maps column_id -> values
43 # -> instances_per_value
44 self._image_pair_dicts = []
45
46 def add_image_pair(self, image_pair):
47 """Adds an ImagePair; this may be repeated any number of times."""
48 # Special handling when we add the first ImagePair...
49 if not self._image_pair_dicts:
50 self._base_url = image_pair.base_url
51
52 if image_pair.base_url != self._base_url:
53 raise Exception('added ImagePair with base_url "%s" instead of "%s"' % (
54 image_pair.base_url, self._base_url))
55 self._image_pair_dicts.append(image_pair.as_dict())
56 extra_columns_dict = image_pair.extra_columns_dict
57 if extra_columns_dict:
58 for column_id, value in extra_columns_dict.iteritems():
commit-bot@chromium.org16f41802014-02-26 19:05:20 +000059 self._add_extra_column_value_to_summary(column_id, value)
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +000060
61 def set_column_header_factory(self, column_id, column_header_factory):
62 """Overrides the default settings for one of the extraColumn headers.
63
64 Args:
65 column_id: string; unique ID of this column (must match a key within
66 an ImagePair's extra_columns dictionary)
67 column_header_factory: a ColumnHeaderFactory object
68 """
69 self._column_header_factories[column_id] = column_header_factory
70
71 def get_column_header_factory(self, column_id):
72 """Returns the ColumnHeaderFactory object for a particular extraColumn.
73
74 Args:
75 column_id: string; unique ID of this column (must match a key within
76 an ImagePair's extra_columns dictionary)
77 """
78 column_header_factory = self._column_header_factories.get(column_id, None)
79 if not column_header_factory:
80 column_header_factory = column.ColumnHeaderFactory(header_text=column_id)
81 self._column_header_factories[column_id] = column_header_factory
82 return column_header_factory
83
commit-bot@chromium.org16f41802014-02-26 19:05:20 +000084 def ensure_extra_column_values_in_summary(self, column_id, values):
85 """Ensure this column_id/value pair is part of the extraColumns summary.
86
87 Args:
88 column_id: string; unique ID of this column
89 value: string; a possible value for this column
90 """
91 for value in values:
92 self._add_extra_column_value_to_summary(
93 column_id=column_id, value=value, addend=0)
94
95 def _add_extra_column_value_to_summary(self, column_id, value, addend=1):
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +000096 """Records one column_id/value extraColumns pair found within an ImagePair.
97
98 We use this information to generate tallies within the column header
99 (how many instances we saw of a particular value, within a particular
100 extraColumn).
commit-bot@chromium.org16f41802014-02-26 19:05:20 +0000101
102 Args:
103 column_id: string; unique ID of this column (must match a key within
104 an ImagePair's extra_columns dictionary)
105 value: string; a possible value for this column
106 addend: integer; how many instances to add to the tally
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +0000107 """
108 known_values_for_column = self._extra_column_tallies.get(column_id, None)
109 if not known_values_for_column:
110 known_values_for_column = {}
111 self._extra_column_tallies[column_id] = known_values_for_column
112 instances_of_this_value = known_values_for_column.get(value, 0)
commit-bot@chromium.org16f41802014-02-26 19:05:20 +0000113 instances_of_this_value += addend
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +0000114 known_values_for_column[value] = instances_of_this_value
115
116 def _column_headers_as_dict(self):
117 """Returns all column headers as a dictionary."""
118 asdict = {}
119 for column_id, values_for_column in self._extra_column_tallies.iteritems():
120 column_header_factory = self.get_column_header_factory(column_id)
121 asdict[column_id] = column_header_factory.create_as_dict(
122 values_for_column)
123 return asdict
124
125 def as_dict(self):
126 """Returns a dictionary describing this package of ImagePairs.
127
128 Uses the KEY__* constants as keys.
129 """
130 return {
commit-bot@chromium.org16f41802014-02-26 19:05:20 +0000131 KEY__EXTRACOLUMNHEADERS: self._column_headers_as_dict(),
commit-bot@chromium.org536b15f2014-02-13 17:17:05 +0000132 KEY__IMAGEPAIRS: self._image_pair_dicts,
133 KEY__IMAGESETS: [{
134 KEY__IMAGESETS__BASE_URL: self._base_url,
135 KEY__IMAGESETS__DESCRIPTION: self._descriptions[0],
136 }, {
137 KEY__IMAGESETS__BASE_URL: self._base_url,
138 KEY__IMAGESETS__DESCRIPTION: self._descriptions[1],
139 }],
140 }