aboutsummaryrefslogtreecommitdiff
blob: cf31d69c6902622554f14ddbd8e2b9fc5db799cb (plain)
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/usr/bin/perl -w
# $Id: countify,v 1.3 2005/05/16 18:10:46 agriffis Exp $
#
# Copyright 2005-2015 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
#
# countify: collect, tabulate and announce ballot results
#

BEGIN {
    my $dirname;
    if(-f '/etc/elections/Votify.pm') {
        $dirname = '/etc/elections';
    } else {
        use Cwd qw(abs_path);
        use File::Basename qw(dirname);
        $dirname = dirname(abs_path(__FILE__));
    }
    push @INC, $dirname;
}

use Cwd qw(abs_path);
use File::Basename;
use File::Spec::Functions;
use Getopt::Long;
use List::Util;
use POSIX;

use Votify 'official';
use strict;

######################################################################
# Global vars
######################################################################

(my $zero = $0) =~ s,.*/,,;
(my $version = $Votify::version) =~ s/.*?(\d.*\d).*/$zero version $1\n/;
my %opt;

# Collect the open elections
my (%open_elections, $usage_elections);
%open_elections = Votify::get_open_elections_hash();
if (scalar keys %open_elections) {
    $usage_elections = join("\n        ", keys %open_elections);
} else {
    $usage_elections = "(no elections currently open)";
}

# rest of usage
my $usage = <<EOT;

usage: $zero <command> <election>

where <command> is one of:

    --collect   Collect the submitted ballots from home directories
    --rank      Show ranking based on master ballot
    --help      Show this help message
    --version   Show version information

and <election> is one of the elections currently in-progress.  The following
elections are currently open:

    $usage_elections

EOT

######################################################################
# Main
######################################################################

package main;

# Make sure umask is secure before we do anything
umask 077;

# Parse the options on the cmdline.  Put the short versions first in
# each optionstring so that the hash keys are created using the short
# versions.  For example, use 'q|qar', not 'qar|q'.
my ($result) = GetOptions(
    \%opt,
    'collect',          # collect the submitted ballots from home directories
    'rank',             # show the ranking based on a master ballot
    'help',             # help message
    'version',          # version information
);
if ($opt{'help'} or not %opt) { print STDERR $usage; exit 0 }
if ($opt{'version'}) { print STDERR $version; exit 0 }
die "$zero: only one command allowed; use --help for help\n" if 1 < keys %opt;
die "$zero: election required; use --help for help\n" unless @ARGV == 1;

my ($election) = $ARGV[0];
my ($vl) = VoterList->new($election);

if ($opt{'collect'}) {
    my ($ol) = OfficialList->new($election);
    my ($master) = MasterBallot->new($election, $vl);
    $master->collect($vl->voters);
    for my $o ($ol->officials) {
        my ($uid, $home) = (getpwnam $o)[2,7];
        $home = "/home/$o" unless defined $home;
        mkdir catfile("$home", "results-$election");
        $master->write_master("$home/results-$election/master-$election");
        $master->write_casting_voters("$home/results-$election/casting-voters-$election");
        $vl->write_confs("$home/results-$election/confs-$election");
        chown $uid, -1, "$home/results-$election",
            "$home/results-$election/master-$election",
            "$home/results-$election/confs-$election",
            "$home/results-$election/casting-voters-$election";
    }
    exit 0;
}

if ($opt{'rank'}) {
    my ($master) = MasterBallot->new($election, $vl);
    my (@candidates, @winner, @ranked, @ranks);
    $master->read_master("$ENV{HOME}/results-$election/master-$election");
    $master->generate_candidates();
    @candidates = sort keys %{$master->{'candidates'}};

    while (1) {
        $master->tabulate();

        # this is a hack :-(
        #print "\n";
        for my $r (@ranked) {
            #print "$r is already ranked\n";
            for my $c (@candidates) {
                $master->{'table'}{"$r+$c"} = -1;
            }
            $master->{'table'}{"$r+$r"} = '+++';
        }

        # now display the table
        print "\n";
        $master->display_table();

        @winner = $master->cssd();
        if (@winner == @candidates) {
            print "\n*** No additional winners to add to the list ***\n";
            last;
        }

        push @ranks, [ @winner ];
        push @ranked, @winner;
        if (@ranked == @candidates) {
            print "\n*** Finished ranking candidates ***\n";
            last;
        }

        print "\n*** Running another pass to find the next winners... ***\n";
    }

    print "\nFinal ranked list:\n";
    print map "@$_\n", @ranks;
}

__END__
# vim:sw=4 et