~launchpad-pqm/launchpad/devel

9265.1.1 by Jonathan Lange
Add lp-dev-tools that lack license confusion, changing the license to match
1
#!/usr/bin/env python
2
#
3
# Copyright 2009 Canonical Ltd.  This software is licensed under the
4
# GNU Affero General Public License version 3 (see the file LICENSE).
5
6
# Use this to calculate priorities based on Wiki priority lists such as
7
# https://launchpad.canonical.com/VersionThreeDotO/Bugs/Inputs
8
9
10
import sys
11
14612.2.6 by William Grant
utilities
12
9265.1.1 by Jonathan Lange
Add lp-dev-tools that lack license confusion, changing the license to match
13
class Row:
14
    def __init__(self, scores, trailing):
15
        self.scores = scores
16
        self.trailing = trailing
17
18
19
def append_average(items, adjusted_scores, row):
20
    if len(adjusted_scores) == 0:
21
        avg = len(rows_of_scores)
22
    else:
23
        avg = sum(adjusted_scores)/len(adjusted_scores)
24
    items.append((avg, "||%4.1f||%s" % (avg, row.trailing)))
25
26
27
def blanks_dont_count(rows_of_scores):
28
    items = []
29
    for row in rows_of_scores:
30
        adjusted_scores = []
31
        for score in row.scores:
32
            if score == -1:
33
                continue
34
            adjusted_scores.append(score)
35
        append_average(items, adjusted_scores, row)
36
    items.sort()
37
    return items
38
39
40
def blanks_are_heavy(rows_of_scores, half=False):
41
    items = []
42
    for row in rows_of_scores:
43
        adjusted_scores = []
44
        for score in row.scores:
45
            if score == -1:
46
                score = len(rows_of_scores)
47
                if half:
48
                    score = score/2
49
            adjusted_scores.append(score)
50
        append_average(items, adjusted_scores, row)
51
    items.sort()
52
    return items
53
54
55
def less_is_more(rows_of_scores):
56
    total_per_column = {}
57
    for row in rows_of_scores:
58
        scores = row.scores
59
        for i in range(0, len(scores)):
60
            score = scores[i]
61
            total_per_column.setdefault(i, 0)
62
            if score != -1:
63
                total_per_column[i] += 1
64
    print total_per_column
65
    items = []
66
    for row in rows_of_scores:
67
        scores = row.scores
68
        adjusted_scores = []
69
        for i in range(0, len(scores)):
70
            score = scores[i]
71
            if score == -1:
72
                score = len(rows_of_scores)
73
            weight = total_per_column[i]
74
            adjusted_scores.append(score*weight)
75
        append_average(items, adjusted_scores, row)
76
    items.sort()
77
    return items
78
79
80
def condorcet(rows_of_scores):
81
    raise NotImplementedError
82
83
84
def parse_scores(str):
85
    rows = []
86
    head = []
87
    tail = []
88
    ate_first_line = False
89
    # We drop the first split element because the line starts with a ||
90
    delta = 1
91
    for s in str:
92
        if not s.strip().startswith("||"):
93
            # Regular output; just output it
94
            if rows:
95
                tail.append(s)
96
            else:
97
                head.append(s)
98
            continue
99
100
        if not ate_first_line:
101
            ate_first_line = True
102
            # Let's take a look at the header
103
            if s.strip().startswith("|| * "):
104
                # Get rid of scores since we're recalculating
105
                delta += 1
106
                head.append(s)
107
            else:
108
                head.append("|| *  " + s)
109
            continue
110
111
        scores = []
112
        columns = s.split("||")[delta:]
113
        for col_idx, score in enumerate(columns):
114
            score = score.strip()
115
            if score:
116
                first_char = score[0]
117
                if not first_char.isdigit() and not first_char == "-":
118
                    # We hit some text, get out of here
119
                    break
120
            try:
121
                score = float(score)
122
            except ValueError:
123
                # If no value was input, we assume it is equivalent to being
124
                # the last option for this voter.
125
                score = -1
126
            scores.append(score)
127
        rows.append(Row(scores, '||'.join(columns)))
128
    return rows, head, tail
129
130
131
if __name__ == "__main__":
132
    str = sys.stdin.read().strip().splitlines()
133
    rows_of_scores, head, tail = parse_scores(str)
134
    if len(sys.argv) > 1 and sys.argv[1] == "less-is-more":
135
        func = less_is_more
136
    elif len(sys.argv) > 1 and sys.argv[1] == "condorcet":
137
        func = condorcet
138
    elif len(sys.argv) > 1 and sys.argv[1] == "blanks-dont-count":
139
        func = blanks_dont_count
140
    else:
141
        func = blanks_are_heavy
142
    items = func(rows_of_scores)
143
    print "\n".join(head)
144
    print "\n".join([i[1] for i in items])
145
    print "\n".join(tail)
146