Skip to content

Majority Poll Result Data

Tool class used to create a results object instance for majority judgment polls.

MajorityPollResultData dataclass

Bases: object

Small class used to store the data related to the results of a majority poll

Source code in apps/votes_results/classes/majority_poll_result_data.py
 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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
@dataclass
class MajorityPollResultData(object):
    """Small class used to store the data related to
    the results of a majority poll"""

    option: PollOptionModel
    """The poll_option the data is related to"""

    good_votes: int
    """The number of good votes performed to this option"""

    median: int
    """The majority grade of the option/poll, called as a median
    of the value of the votes"""

    bad_votes: int
    """The number of bad votes performed to this option"""

    positive_grade: bool
    """The majority grade sign is '+' if good votes are more than
    bad votes"""

    option_votes: QuerySet
    """All the votes for this option"""

    def __init__(self, option: PollOptionModel):

        self.option = option

        # retrieve votes (ordered by rating)
        self.option_votes = MajorityJudgmentModel.objects \
            .filter(poll_option=option.id) \
            .order_by('rating')

        if self.option_votes.count() < 1:
            raise PollNotYetVodedException()

        # calculate median (or worse of two)
        self.median = self.option_votes[math.floor((self.option_votes.count()-1)/2)].rating

        # retrieve number of (strictly) greater and smaller votes
        self.good_votes: int = self.option_votes.filter(rating__gt=self.median).count()
        self.bad_votes: int = self.option_votes.filter(rating__lt=self.median).count()

        # set sign: 
        # if good > bad         --> +
        # else if good <= bad   --> -
        self.positive_grade = (self.good_votes > self.bad_votes)

    def get_qualitative_median(self) -> str:
        """Get median value as a qualitative judjment"""
        return self.get_qualitative(self.median)

    def get_qualitative(self, rating) -> str:
        range = ['Pessimo', 'Insufficiente', 'Sufficiente', 'Buono', 'Ottimo']
        return range[rating-1]

    def get_sign(self) -> str:
        """Get sign (as symbol)"""
        return "+" if self.positive_grade else "-"

    def get_judjment_percentages(self) -> list[dict]: 
        """Get percentage of judjments of each value"""

        all_votes = MajorityJudgmentModel.objects \
            .filter(poll_option=self.option)

        all_votes_n = float(all_votes.count())

        colors = ['#E41A1C', '#FE8E3C', '#FFFFCD', '#7FCEBC', '#253495']
        textcolors = ['white', 'white', 'black', 'black', 'white']

        res: list[dict] = []
        for i in range(1,6):
            value = float(all_votes.filter(rating=i).count())/all_votes_n
            if value > 0:
                res.append({
                    'value': value, 
                    'percentage': int(value*100), 
                    'style': f"background-color:{colors[i-1]}; color: {textcolors[i-1]}; ", 
                    'label': self.get_qualitative(i), 
                })

        return res


    def __eq__(self, other): 

        if not isinstance(other, MajorityPollResultData):
            raise Exception("Cannot compare MajorityPollResultData with other type")

        return self.compare(other) == 0

    def __gt__(self, other):
        """
        Check if my rating is better than other
        (so if I am semantically ">" than the other one)
        """

        if not isinstance(other, MajorityPollResultData):
            raise Exception("Cannot compare MajorityPollResultData with other type")

        cmp = self.compare(other)
        return cmp > 0 if cmp != 0 else self.option.value > other.option.value

    def majority_values_median(self, values: list[MajorityJudgmentModel]) -> int:
        """Returns new median from list of majority values"""

        old_median = values[math.floor(len(values)/2)]

        if len(values) > 1:
            # here we exclude the single value of the median
            values.remove(old_median)
            new_median = values[math.floor(len(values)/2)].rating

            return new_median
        else:
            return old_median

    def median_value(self, iteration=0) -> int:
        """Calculates the median of the current majority values iteration"""

        majority_values = list(self.option_votes)
        majority_values.sort(key=lambda x: x.rating)

        median_idx = math.floor((len(majority_values)-1)/2)
        median = majority_values[median_idx].rating

        while iteration > 0:
            del majority_values[median_idx]
            iteration -= 1 
            median_idx = math.floor((len(majority_values)-1)/2)
            median = majority_values[median_idx].rating

        return median

    def sorting(self, obj, i) -> bool:
        """Function that gives sorting rules for Majority Poll Result Data Objects"""

        if i == self.option_votes.count():
            return self.option.value < obj.option.value

        # if median is greater --> x win
        if self.median_value(iteration=i) > obj.median_value(iteration=i):
            return True
        elif self.median_value(iteration=i) < obj.median_value(iteration=i):
            return False
        else:
            return self.sorting(obj, i+1)

    def compare(self, obj, i=0):
        """returns 1 if self > obj, -1 if self < obj, 0 if self == obj"""

        if i == self.option_votes.count():
            return 0

        # if median is greater --> x win
        self_median = self.median_value(iteration=i)
        obj_median = obj.median_value(iteration=i)

        if self_median > obj_median:
            return +1
        elif self_median < obj_median:
            return -1
        else:
            return self.compare(obj, i+1)

bad_votes: int = self.option_votes.filter(rating__lt=self.median).count() class-attribute instance-attribute

The number of bad votes performed to this option

good_votes: int = self.option_votes.filter(rating__gt=self.median).count() class-attribute instance-attribute

The number of good votes performed to this option

median = self.option_votes[math.floor(self.option_votes.count() - 1 / 2)].rating class-attribute instance-attribute

The majority grade of the option/poll, called as a median of the value of the votes

option = option class-attribute instance-attribute

The poll_option the data is related to

option_votes = MajorityJudgmentModel.objects.filter(poll_option=option.id).order_by('rating') class-attribute instance-attribute

All the votes for this option

positive_grade = self.good_votes > self.bad_votes class-attribute instance-attribute

The majority grade sign is '+' if good votes are more than bad votes

__gt__(other)

Check if my rating is better than other (so if I am semantically ">" than the other one)

Source code in apps/votes_results/classes/majority_poll_result_data.py
103
104
105
106
107
108
109
110
111
112
113
def __gt__(self, other):
    """
    Check if my rating is better than other
    (so if I am semantically ">" than the other one)
    """

    if not isinstance(other, MajorityPollResultData):
        raise Exception("Cannot compare MajorityPollResultData with other type")

    cmp = self.compare(other)
    return cmp > 0 if cmp != 0 else self.option.value > other.option.value

compare(obj, i=0)

returns 1 if self > obj, -1 if self < obj, 0 if self == obj

Source code in apps/votes_results/classes/majority_poll_result_data.py
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
def compare(self, obj, i=0):
    """returns 1 if self > obj, -1 if self < obj, 0 if self == obj"""

    if i == self.option_votes.count():
        return 0

    # if median is greater --> x win
    self_median = self.median_value(iteration=i)
    obj_median = obj.median_value(iteration=i)

    if self_median > obj_median:
        return +1
    elif self_median < obj_median:
        return -1
    else:
        return self.compare(obj, i+1)

get_judjment_percentages()

Get percentage of judjments of each value

Source code in apps/votes_results/classes/majority_poll_result_data.py
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def get_judjment_percentages(self) -> list[dict]: 
    """Get percentage of judjments of each value"""

    all_votes = MajorityJudgmentModel.objects \
        .filter(poll_option=self.option)

    all_votes_n = float(all_votes.count())

    colors = ['#E41A1C', '#FE8E3C', '#FFFFCD', '#7FCEBC', '#253495']
    textcolors = ['white', 'white', 'black', 'black', 'white']

    res: list[dict] = []
    for i in range(1,6):
        value = float(all_votes.filter(rating=i).count())/all_votes_n
        if value > 0:
            res.append({
                'value': value, 
                'percentage': int(value*100), 
                'style': f"background-color:{colors[i-1]}; color: {textcolors[i-1]}; ", 
                'label': self.get_qualitative(i), 
            })

    return res

get_qualitative_median()

Get median value as a qualitative judjment

Source code in apps/votes_results/classes/majority_poll_result_data.py
59
60
61
def get_qualitative_median(self) -> str:
    """Get median value as a qualitative judjment"""
    return self.get_qualitative(self.median)

get_sign()

Get sign (as symbol)

Source code in apps/votes_results/classes/majority_poll_result_data.py
67
68
69
def get_sign(self) -> str:
    """Get sign (as symbol)"""
    return "+" if self.positive_grade else "-"

majority_values_median(values)

Returns new median from list of majority values

Source code in apps/votes_results/classes/majority_poll_result_data.py
115
116
117
118
119
120
121
122
123
124
125
126
127
def majority_values_median(self, values: list[MajorityJudgmentModel]) -> int:
    """Returns new median from list of majority values"""

    old_median = values[math.floor(len(values)/2)]

    if len(values) > 1:
        # here we exclude the single value of the median
        values.remove(old_median)
        new_median = values[math.floor(len(values)/2)].rating

        return new_median
    else:
        return old_median

median_value(iteration=0)

Calculates the median of the current majority values iteration

Source code in apps/votes_results/classes/majority_poll_result_data.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
def median_value(self, iteration=0) -> int:
    """Calculates the median of the current majority values iteration"""

    majority_values = list(self.option_votes)
    majority_values.sort(key=lambda x: x.rating)

    median_idx = math.floor((len(majority_values)-1)/2)
    median = majority_values[median_idx].rating

    while iteration > 0:
        del majority_values[median_idx]
        iteration -= 1 
        median_idx = math.floor((len(majority_values)-1)/2)
        median = majority_values[median_idx].rating

    return median

sorting(obj, i)

Function that gives sorting rules for Majority Poll Result Data Objects

Source code in apps/votes_results/classes/majority_poll_result_data.py
146
147
148
149
150
151
152
153
154
155
156
157
158
def sorting(self, obj, i) -> bool:
    """Function that gives sorting rules for Majority Poll Result Data Objects"""

    if i == self.option_votes.count():
        return self.option.value < obj.option.value

    # if median is greater --> x win
    if self.median_value(iteration=i) > obj.median_value(iteration=i):
        return True
    elif self.median_value(iteration=i) < obj.median_value(iteration=i):
        return False
    else:
        return self.sorting(obj, i+1)