~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env python
#
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

# Use this to calculate priorities based on Wiki priority lists such as
# https://launchpad.canonical.com/VersionThreeDotO/Bugs/Inputs


import sys

class Row:
    def __init__(self, scores, trailing):
        self.scores = scores
        self.trailing = trailing


def append_average(items, adjusted_scores, row):
    if len(adjusted_scores) == 0:
        avg = len(rows_of_scores)
    else:
        avg = sum(adjusted_scores)/len(adjusted_scores)
    items.append((avg, "||%4.1f||%s" % (avg, row.trailing)))


def blanks_dont_count(rows_of_scores):
    items = []
    for row in rows_of_scores:
        adjusted_scores = []
        for score in row.scores:
            if score == -1:
                continue
            adjusted_scores.append(score)
        append_average(items, adjusted_scores, row)
    items.sort()
    return items


def blanks_are_heavy(rows_of_scores, half=False):
    items = []
    for row in rows_of_scores:
        adjusted_scores = []
        for score in row.scores:
            if score == -1:
                score = len(rows_of_scores)
                if half:
                    score = score/2
            adjusted_scores.append(score)
        append_average(items, adjusted_scores, row)
    items.sort()
    return items


def less_is_more(rows_of_scores):
    total_per_column = {}
    for row in rows_of_scores:
        scores = row.scores
        for i in range(0, len(scores)):
            score = scores[i]
            total_per_column.setdefault(i, 0)
            if score != -1:
                total_per_column[i] += 1
    print total_per_column
    items = []
    for row in rows_of_scores:
        scores = row.scores
        adjusted_scores = []
        for i in range(0, len(scores)):
            score = scores[i]
            if score == -1:
                score = len(rows_of_scores)
            weight = total_per_column[i]
            adjusted_scores.append(score*weight)
        append_average(items, adjusted_scores, row)
    items.sort()
    return items


def condorcet(rows_of_scores):
    raise NotImplementedError


def parse_scores(str):
    rows = []
    head = []
    tail = []
    ate_first_line = False
    # We drop the first split element because the line starts with a ||
    delta = 1
    for s in str:
        if not s.strip().startswith("||"):
            # Regular output; just output it
            if rows:
                tail.append(s)
            else:
                head.append(s)
            continue

        if not ate_first_line:
            ate_first_line = True
            # Let's take a look at the header
            if s.strip().startswith("|| * "):
                # Get rid of scores since we're recalculating
                delta += 1
                head.append(s)
            else:
                head.append("|| *  " + s)
            continue

        scores = []
        columns = s.split("||")[delta:]
        for col_idx, score in enumerate(columns):
            score = score.strip()
            if score:
                first_char = score[0]
                if not first_char.isdigit() and not first_char == "-":
                    # We hit some text, get out of here
                    break
            try:
                score = float(score)
            except ValueError:
                # If no value was input, we assume it is equivalent to being
                # the last option for this voter.
                score = -1
            scores.append(score)
        rows.append(Row(scores, '||'.join(columns)))
    return rows, head, tail


if __name__ == "__main__":
    str = sys.stdin.read().strip().splitlines()
    rows_of_scores, head, tail = parse_scores(str)
    if len(sys.argv) > 1 and sys.argv[1] == "less-is-more":
        func = less_is_more
    elif len(sys.argv) > 1 and sys.argv[1] == "condorcet":
        func = condorcet
    elif len(sys.argv) > 1 and sys.argv[1] == "blanks-dont-count":
        func = blanks_dont_count
    else:
        func = blanks_are_heavy
    items = func(rows_of_scores)
    print "\n".join(head)
    print "\n".join([i[1] for i in items])
    print "\n".join(tail)