public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
From: "Jorge Manuel B. S. Vicetto" <jmbsvicetto@gentoo.org>
To: gentoo-commits@lists.gentoo.org
Subject: [gentoo-commits] proj/elections:master commit in: council-201306/, trustees-201306/
Date: Fri, 28 Jun 2013 12:52:00 +0000 (UTC)	[thread overview]
Message-ID: <1372423759.76c33523cfa735aa70a42a3913a4878db4fa57af.jmbsvicetto@gentoo> (raw)

commit:     76c33523cfa735aa70a42a3913a4878db4fa57af
Author:     Jorge Manuel B. S. Vicetto (jmbsvicetto) <jmbsvicetto <AT> gentoo <DOT> org>
AuthorDate: Fri Jun 28 12:49:19 2013 +0000
Commit:     Jorge Manuel B. S. Vicetto <jmbsvicetto <AT> gentoo <DOT> org>
CommitDate: Fri Jun 28 12:49:19 2013 +0000
URL:        http://sources.gentoo.org/gitweb/?p=proj/elections.git;a=commit;h=76c33523

Initial commit for the council-201306 and trustees-201306 elections.

---
 council-201306/Votify.pm                  |   1 +
 council-201306/ballot-council-201306      |   1 +
 council-201306/election-details           |   6 +
 council-201306/get-devs-list              |   2 +
 council-201306/officials-council-201306   |   5 +
 council-201306/voters-council-201306      | 239 +++++++++++
 trustees-201306/Votify.pm                 | 684 ++++++++++++++++++++++++++++++
 trustees-201306/ballot-trustees-201306    |   0
 trustees-201306/election-details          |   7 +
 trustees-201306/officials-trustees-201306 |   4 +
 10 files changed, 949 insertions(+)

diff --git a/council-201306/Votify.pm b/council-201306/Votify.pm
new file mode 120000
index 0000000..a6f4461
--- /dev/null
+++ b/council-201306/Votify.pm
@@ -0,0 +1 @@
+../Votify.pm
\ No newline at end of file

diff --git a/council-201306/ballot-council-201306 b/council-201306/ballot-council-201306
new file mode 100644
index 0000000..e8d662b
--- /dev/null
+++ b/council-201306/ballot-council-201306
@@ -0,0 +1 @@
+_reopen_nominations

diff --git a/council-201306/election-details b/council-201306/election-details
new file mode 100644
index 0000000..d7b896a
--- /dev/null
+++ b/council-201306/election-details
@@ -0,0 +1,6 @@
+name: council-201306
+startDate: 2013-06-30 00:00:00 UTC
+endDate: 2013-07-14 00:00:01 UTC
+officials: antarus, jmbsvicetto, neddyseagoon, robbat2, tampakrap
+voters: http://www.gentoo.org/proj/en/elections/council/2013/voters-council-201306.txt
+ballot: http://www.gentoo.org/proj/en/elections/council/2013/ballot-council-201306.txt

diff --git a/council-201306/get-devs-list b/council-201306/get-devs-list
new file mode 100644
index 0000000..ce27f5c
--- /dev/null
+++ b/council-201306/get-devs-list
@@ -0,0 +1,2 @@
+ENAME=$(awk '/^name:/{print $2}' election-details)
+ldapsearch '(&(objectClass=gentooDev)(gentooStatus=active)(!(gentooAccess=infra-system.group)))' -Z uid -LLL -S uid | awk '/^uid:/ {print $2}' >${ENAME}-devs-list

diff --git a/council-201306/officials-council-201306 b/council-201306/officials-council-201306
new file mode 100644
index 0000000..f5ce904
--- /dev/null
+++ b/council-201306/officials-council-201306
@@ -0,0 +1,5 @@
+antarus
+jmbsvicetto
+neddyseagoon
+robbat2
+tampakrap

diff --git a/council-201306/voters-council-201306 b/council-201306/voters-council-201306
new file mode 100644
index 0000000..947a54f
--- /dev/null
+++ b/council-201306/voters-council-201306
@@ -0,0 +1,239 @@
+a3li
+aballier
+abcd
+ackle
+agaffney
+ago
+aidecoe
+alexxy
+alonbl
+anarchy
+angelos
+antarus
+araujo
+armin76
+axs
+betelgeuse
+bicatali
+billie
+binki
+blackace
+blueboar
+blueness
+c1pher
+calchan
+cardoe
+caster
+cedk
+chainsaw
+chiguire
+chithanh
+chutzpah
+constanze
+craig
+creffett
+d2_racing
+dabbott
+dagger
+dang
+darkside
+dastergon
+dberkholz
+deathwing00
+desultory
+dev-zero
+dilfridge
+dirtyepic
+djay
+djc
+dolsen
+earthwings
+elvanor
+eras
+eva
+fauli
+ferringb
+flameeyes
+flammie
+floppym
+ford_prefect
+fordfrog
+fox
+fuzzyray
+gengor
+george
+gienah
+gmsoft
+graaff
+gregkh
+grobian
+grozin
+gurligebis
+halcy0n
+hanno
+hasufell
+hattya
+haubi
+hd_brummy
+heroxbd
+hkbst
+hollow
+hparker
+hwoarang
+i92guboj
+idella4
+idl0r
+ikelos
+iksaif
+isaiah
+jaervosz
+jbartosik
+jdhore
+je_fro
+jer
+jkt
+jlec
+jmbsvicetto
+jmorgan
+john_r_graham
+johu
+joker
+josejx
+jsbronder
+kallamej
+ken69267
+kensington
+keri
+kernelsensei
+keytoaster
+kingtaco
+klausman
+klieber
+klondike
+kumba
+lack
+leio
+lejonet
+lh
+lordvan
+lu_zero
+lxnay
+mabi
+maekke
+maksbotan
+marienz
+matsl
+matsuu
+mattm
+mattst88
+mduft
+mgorny
+miknix
+miska
+moult
+mpagano
+mr_bones_
+mschiff
+naota
+nathanzachary
+nativemad
+neddyseagoon
+nerdboy
+neurogeek
+nightmorph
+nimiux
+nirbheek
+nixnut
+nixphoeni
+olemarkus
+ottxor
+pacho
+patrick
+pauldv
+pchrist
+pebenito
+peper
+pesa
+phajdan.jr
+phosphan
+pilla
+pinkbyte
+pjp
+polynomial-c
+prometheanfire
+psomas
+pva
+pvdabeel
+qiaomuf
+qnikst
+quantumsummers
+r0bertz
+radhermit
+rafaelmartins
+rajiv
+ramereth
+rane
+ranger
+reavertm
+remi
+rich0
+robbat2
+ryao
+s4t4n
+sbriesen
+scarabeus
+sera
+serkan
+slis
+slyfox
+sochotnicky
+solar
+sping
+spiros
+ssuominen
+steev
+suka
+swegener
+swift
+tampakrap
+tester
+tetromino
+tgurr
+the_paya
+thev00d00
+think4urs11
+timebandit
+titanofold
+tomjbe
+tomk
+tomka
+tommy
+tomwij
+tove
+trapni
+tristan
+tupone
+ulm
+ultrabug
+underling
+vadimk
+vapier
+vikraman
+vincent
+vostorga
+voyageur
+weaver
+williamh
+wired
+wormo
+wschlich
+xarthisius
+xmw
+yac
+yngwin
+zerochaos
+zlogene
+zmedico
+zorry
+zx2c4
+zzam

diff --git a/trustees-201306/Votify.pm b/trustees-201306/Votify.pm
new file mode 100644
index 0000000..c99685d
--- /dev/null
+++ b/trustees-201306/Votify.pm
@@ -0,0 +1,684 @@
+# $Id: Votify.pm,v 1.5 2005/05/16 23:58:09 agriffis Exp $
+#
+# Copyright 2005 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+# votify.pm: common classes for votify and countify
+#
+
+package Votify;
+
+use POSIX;
+use List::Util;
+use strict;
+
+our ($datadir) = '/etc/elections/current';
+(our $zero = $0) =~ s,.*/,,;
+
+sub import {
+    my ($class, $mode) = @_;
+    $Votify::mode = $mode;
+}
+
+######################################################################
+# OfficialList
+######################################################################
+
+package OfficialList;
+
+sub new {
+    my ($class, $election) = @_;
+    my ($self) = {
+        election => $election,
+        officials => [],
+    };
+
+    # no point in waiting to load
+    open(F, "<$Votify::datadir/officials-$election") 
+        or die("failed to open officials file");
+    chomp(@{$self->{'officials'}} = <F>);
+    close(F);
+
+    bless $self, $class;
+    return $self;
+}
+
+sub officials {
+    my ($self) = @_;
+    @{$self->{'officials'}};
+}
+
+######################################################################
+# VoterList
+######################################################################
+
+package VoterList;
+
+sub new {
+    my ($class, $election) = @_;
+    my (@voterlist, $r);
+    my ($self) = {
+        election => $election,
+        default_filename => "$Votify::datadir/confs-$election",
+        filename => '',
+        voters => {},   # confnum => voter
+        confs => {},    # voter => confnum
+    };
+
+    # no point in waiting to load
+    open(F, "<$Votify::datadir/voters-$election") 
+        or die("failed to open voters file");
+    chomp(@voterlist = <F>);
+    close(F);
+
+    # assign confirmation numbers randomly
+    for my $v (@voterlist) {
+        do { $r = int rand 0xffff } while exists $self->{'voters'}{$r};
+        $self->{'voters'}{$r} = $v;
+        $self->{'confs'}{$v} = $r;
+    }
+
+    unless (keys %{$self->{'voters'}} == keys %{$self->{'confs'}}) {
+        die("discrepancy deteced in VoterList");
+    }
+
+    bless $self, $class;
+    return $self;
+}
+
+sub confs {
+    my ($self) = @_;
+    sort keys %{$self->{'voters'}};
+}
+
+sub voters {
+    my ($self) = @_;
+    sort keys %{$self->{'confs'}};
+}
+
+sub getvoter {
+    my ($self, $conf) = @_;
+    return $self->{'voters'}{$conf};
+}
+
+sub getconf {
+    my ($self, $voter) = @_;
+    return $self->{'confs'}{$voter};
+}
+
+sub write {
+    my ($self, $filename) = @_;
+
+    $filename ||= $self->{'default_filename'};
+    $self->{'filename'} = $filename;
+
+    if (-f $filename) {
+        die "$filename already exists; please remove it first";
+    }
+
+    open(F, ">$filename") or die("can't write to $filename");
+    for my $c ($self->confs) {
+        printf F "%04x %s\n", $c, $self->getvoter($c);
+    }
+    close F;
+}
+
+######################################################################
+# MasterBallot
+######################################################################
+
+package MasterBallot;
+
+use Data::Dumper;
+
+sub new {
+    my ($class, $election, $vl) = @_;
+    my ($self) = {
+        election => $election,
+        default_filename => "$Votify::datadir/master-$election",
+        filename => '',
+        voterlist => $vl,
+        ballots => {},          # indexed by conf num
+        candidates => undef,    # indexed by long name
+        table => undef,         # indexed by row+column
+    };
+
+    bless $self, $class;
+    return $self;
+}
+
+sub collect {
+    my ($self, @voters) = @_;
+    my ($c, $v, $home, @pwentry);
+
+    for my $v (@voters) {
+        unless (defined ($c = $self->{'voterlist'}->getconf($v))) {
+            die "$v does not correspond to any confirmation number";
+        }
+
+        @pwentry = getpwnam($v);
+        if(@pwentry) {
+            $home = $pwentry[7];
+        } else {
+            print STDERR "Warning: Assuming /home/$v/ for unknown user: $v\n";
+            $home = sprintf '/home/%s/',$v;
+        }
+
+        unless (-d $home) {
+            print STDERR "Warning: no directory: $home\n";
+            next;
+        }
+
+        if (-f "$home/.ballot-$self->{election}-submitted") {
+            my ($b) = Ballot->new($self->{'election'});
+            $b->read("$home/.ballot-$self->{election}-submitted");
+            if ($b->verify) {
+                print STDERR "Errors found in ballot: $v\n";
+                next;
+            }
+            $self->{'ballots'}{$c} = $b;
+        }
+        elsif (-f "$home/.ballot-$self->{election}") {
+            print STDERR "Warning: $v did not submit their ballot\n";
+        }
+    }
+}
+
+sub write {
+    my ($self, $filename) = @_;
+
+    $filename ||= $self->{'default_filename'};
+    $self->{'filename'} = $filename;
+
+    if (-f $filename) {
+        die "$filename already exists; please remove it first";
+    }
+
+    open(F, ">$filename") or die("can't write to $filename");
+    for my $c (sort keys %{$self->{'ballots'}}) {
+        printf F "--------- confirmation %04x ---------\n", $c;
+        print F $self->{'ballots'}{$c}->to_s
+    }
+    close F;
+}
+
+sub read {
+    my ($self, $filename) = @_;
+    my ($election, $entries) = $self->{'election'};
+
+    $filename ||= $self->{'default_filename'};
+    $self->{'filename'} = $filename;
+
+    open(F, "<$filename") or die("can't read $filename");
+    { local $/ = undef; $entries = <F>; }
+    for my $e (split /^--------- confirmation /m, $entries) {
+        next unless $e; # skip the first zero-length record
+        unless ($e =~ /^([[:xdigit:]]{4}) ---------\n(.*)$/s) {
+            die "error parsing entry:\n$e";
+        }
+        my ($c, $s, $b) = ($1, $2, Ballot->new($election));
+        $b->from_s($s);
+        $self->{'ballots'}{hex($c)} = $b;
+    }
+}
+
+sub generate_candidates {
+    my ($self) = @_;
+    my ($B, @C, $s);
+
+    # nb: would need to scan all the ballots to support write-ins
+    $B = Ballot->new($self->{'election'});
+    $B->populate;
+    @C = sort map $_->[0], @{$B->choices};
+    for my $c (@C) {
+        $s = $c; # in case $c is shorter than 5 chars
+        for (my $i=5; $i<=length($c); $i++) {
+            $s = substr $c, 0, $i;
+            print join(" ", grep(/^$s/, @C)), "\n";
+            last unless grep(/^$s/, @C) > 1;
+        }
+        $self->{'candidates'}{$c} = $s;
+    }
+}
+
+sub tabulate {
+    my ($self) = @_;
+    my (@candidates);   # full candidate list
+    my (%table);        # resulting table, row.colum where row defeats column
+    $self->{'table'} = \%table;
+
+    $self->generate_candidates unless $self->{'candidates'};
+    @candidates = keys %{$self->{'candidates'}};
+    for my $c1 (@candidates) {
+        for my $c2 (@candidates) {
+            $table{"$c1+$c2"} = 0;
+        }
+        $table{"$c1+$c1"} = '***';
+    }
+
+    # generate the table first;
+    # walk through the ballots, tallying the rankings expressed by each ballot
+    for my $b (values %{$self->{'ballots'}}) {
+        my (@choices, %ranks);
+
+        #print "looking at ballot:\n", $b->to_s, "\n";
+
+        # first determine the ranking of each candidate.  default ranking is
+        # scalar @candidates.
+        @choices = @{$b->choices};
+        @ranks{@candidates} = (scalar @candidates) x @candidates;
+        #print "ranks before determining:\n", Dumper(\%ranks);
+        for (my $i = 0; $i < @choices; $i++) {
+            @ranks{@{$choices[$i]}} = ($i) x @{$choices[$i]};
+        }
+        #print "ranks after determining:\n", Dumper(\%ranks);
+
+        # second add the results of all the pairwise races into our table
+        for my $c1 (@candidates) {
+            for my $c2 (@candidates) {
+                next if $c1 eq $c2;
+                $table{"$c1+$c2"}++ if $ranks{$c1} < $ranks{$c2};
+            }
+        }
+        #print "table after adding:\n";
+        #$self->display_table;
+    }
+}
+
+sub display_table {
+    my ($self) = @_;
+    my (@longnames, @shortnames);
+    my ($minlen, $maxlen, $formatstr) = (0, 4, '');
+
+    $self->generate_candidates unless $self->{'candidates'};
+    @longnames = sort keys %{$self->{'candidates'}};
+    @shortnames = sort values %{$self->{'candidates'}};
+    $minlen = length scalar keys %{$self->{'ballots'}};
+    $minlen = 5 if $minlen < 5;
+    
+    # build the format string
+    for my $s (@shortnames) {
+        if (length($s) > $minlen) {
+            $formatstr .= "  %" . length($s) . "s";
+        } else {
+            $formatstr .= "  %${minlen}s";
+        }
+    }
+    map { $maxlen = length($_) if length($_) > $maxlen } @longnames;
+
+    # prepend the row header; append newline
+    $formatstr = "%${maxlen}s" . $formatstr . "\n";
+
+    # column headers
+    printf $formatstr, '', @shortnames;
+
+    # rows
+    for my $l (@longnames) {
+        printf $formatstr, $l, @{$self->{'table'}}{map "$l+$_", @longnames};
+    }
+}
+
+# utility for cssd
+sub defeats {
+    my ($self, $o1, $o2) = @_;
+    return 0 if $o1 eq $o2;
+    $self->{'table'}{"$o1+$o2"} > $self->{'table'}{"$o2+$o1"};
+}
+
+# utility for cssd
+sub is_weaker_defeat {
+    my ($self, $A, $X, $B, $Y) = @_;
+    die unless $self->defeats($A, $X);
+    die unless $self->defeats($B, $Y);
+    return (
+        $self->{'table'}{"$A+$X"} < $self->{'table'}{"$B+$Y"} or
+        (
+            $self->{'table'}{"$A+$X"} == $self->{'table'}{"$B+$Y"} and
+            $self->{'table'}{"$X+$A"} > $self->{'table'}{"$Y+$B"}
+        )
+    );
+}
+
+sub cssd {
+    my ($self) = @_;
+    my (@candidates);
+
+    @candidates = sort keys %{$self->{'candidates'}};
+
+    while (1) {
+        my (%transitive_defeats);
+        my (@active, @plist);
+
+        ######################################################################
+        # 5. From the list of [undropped] pairwise defeats, we generate a
+        #    set of transitive defeats.
+        #     1. An option A transitively defeats an option C if A
+        #        defeats C or if there is some other option B where A
+        #        defeats B AND B transitively defeats C.
+        for my $o1 (@candidates) {
+            for my $o2 (@candidates) {
+                $transitive_defeats{"$o1+$o2"} = 1 if $self->defeats($o1, $o2);
+            }
+        }
+        for my $i (@candidates) {
+            for my $j (@candidates) {
+                for my $k (@candidates) {
+                    if (exists $transitive_defeats{"$j+$i"} and
+                        exists $transitive_defeats{"$i+$k"})
+                    {
+                        $transitive_defeats{"$j+$k"} = 1;
+                    }
+                }
+            }
+        }
+
+        ######################################################################
+        # 6. We construct the Schwartz set from the set of transitive
+        #    defeats.
+        #     1. An option A is in the Schwartz set if for all options B,
+        #        either A transitively defeats B, or B does not
+        #        transitively defeat A.
+        print "\n";
+        A: for my $A (@candidates) {
+            for my $B (@candidates) {
+                next if $transitive_defeats{"$A+$B"} or not $transitive_defeats{"$B+$A"};
+                # countify marks entries +++ instead of *** when they've already
+                # been ranked.
+                if ($self->{'table'}{"$A+$A"} eq '***') {
+                    print "option $A is eliminated ($B trans-defeats $A, and $A does not trans-defeat $B)\n";
+                }
+                next A;
+            }
+            push @active, $A;
+        }
+        print "the Schwartz set is {", join(", ", @active), "}\n";
+        @candidates = @active;
+
+        ######################################################################
+        # 7. If there are defeats between options in the Schwartz set, we
+        #    drop the weakest such defeats from the list of pairwise
+        #    defeats, and return to step 5.
+        #     1. A defeat (A,X) is weaker than a defeat (B,Y) if V(A,X)
+        #        is less than V(B,Y). Also, (A,X) is weaker than (B,Y) if
+        #        V(A,X) is equal to V(B,Y) and V(X,A) is greater than V
+        #        (Y,B).
+        #     2. A weakest defeat is a defeat that has no other defeat
+        #        weaker than it. There may be more than one such defeat.
+        for my $o1 (@candidates) {
+            for my $o2 (@candidates) {
+                push @plist, [ $o1, $o2 ] if $self->defeats($o1, $o2);
+            }
+        }
+        last unless @plist;
+        @plist = sort {
+            return -1 if $self->is_weaker_defeat(@$a, @$b);
+            return +1 if $self->is_weaker_defeat(@$b, @$a);
+            return 0;
+        } @plist;
+        for my $dx (@plist) {
+            my ($o1, $o2) = @$dx;
+            print("$o1+$o2 ",
+                $self->{'table'}{"$o1+$o2"}, " $o2+$o1 ",
+                $self->{'table'}{"$o2+$o1"}, "\n");
+        }
+        my ($o1, $o2) = @{$plist[0]};
+        $self->{'table'}{"$o1+$o2"} = 0;
+        $self->{'table'}{"$o2+$o1"} = 0;
+    }
+
+    ######################################################################
+    # 8. If there are no defeats within the Schwartz set, then the
+    #    winner is chosen from the options in the Schwartz set. If
+    #    there is only one such option, it is the winner. If there
+    #    are multiple options, the elector with the casting vote
+    #    chooses which of those options wins.
+    print "\n";
+    if (@candidates > 1) {
+        print "result: tie between options ", join(", ", @candidates), "\n";
+    } else {
+        print "result: option @candidates wins\n";
+    }
+
+    return @candidates;
+}
+
+######################################################################
+# Ballot
+######################################################################
+
+package Ballot;
+
+sub new {
+    my ($class, $election) = @_;
+    my ($self) = {
+        election => $election,
+        filename => '',
+        default_filename => $ENV{'HOME'}."/.ballot-$election",
+        choices => [],
+    };
+
+    # Bless me, I'm a ballot!
+    bless $self, $class;
+    return $self;
+}
+
+sub from_s {
+    my ($self, $s) = @_;
+    my (@choices);
+
+    for (split "\n", $s) {
+        s/#.*//;
+        next unless /\S/;
+        push @choices, [ split(' ', $_) ];
+    }
+    die("No data in string") unless @choices;
+
+    $self->{'choices'} = \@choices;
+}
+
+sub read {
+    my ($self, $filename) = @_;
+
+    $filename ||= $self->{'default_filename'};
+    $self->{'filename'} = $filename;
+
+    # Load the data file
+    open(F, "<$filename") or die("couldn't open $filename");
+    { local $/ = undef; $self->from_s(<F>); }
+    close(F);
+}
+
+sub populate {
+    my ($self) = @_;
+    $self->read("$Votify::datadir/ballot-$self->{election}");
+    @{$self->{'choices'}} = List::Util::shuffle(@{$self->{'choices'}});
+}
+
+sub choices {
+    my ($self) = @_;
+    $self->{'choices'};
+}
+
+sub write {
+    my ($self, $filename) = @_;
+
+    if ($Votify::mode ne 'user') {
+        die("we don't write ballots in official mode");
+    }
+
+    $filename ||= $self->{'default_filename'};
+    $self->{'filename'} = $filename;
+
+    # Don't ever overwrite a ballot
+    die("File already exists; please remove $filename\n") if -e $filename;
+
+    # Write the user's ballot
+    open(F, ">$filename") or die "Failed writing $filename";
+    print F <<EOT;
+# This is a ballot for the $self->{election} election.
+# Please rank your choices in order; first choice at the top and last choice at
+# the bottom.  You can put choices on the same line to indicate no preference
+# between them.  Any choices you omit from this file are implicitly added at the
+# end.
+#
+# When you're finished editing this, the next step is to verify your ballot
+# with:
+#
+#   $Votify::zero --verify $self->{election}
+#
+# When that passes and you're satisfied, the final step is to submit your vote:
+#
+#   $Votify::zero --submit $self->{election}
+#
+
+EOT
+    for (@{$self->{'choices'}}) { print F "@$_\n"; }
+    close(F);
+}
+
+sub verify {
+    my ($self) = @_;
+    my (%h, $master, %mh);
+    my (@dups, @missing, @extra);
+    my ($errors_found);
+
+    # Load %h from the user's ballot
+    for my $line (@{$self->{'choices'}}) {
+        for my $entry (@$line) {
+            $h{$entry}++;
+        }
+    }
+
+    # Load the master ballot into another hash and compare them.
+    # The master ballots always do one entry per line, making this a little
+    # easier.
+    $master = Ballot->new($self->{'election'});
+    $master->populate;
+    %mh = map(($_->[0] => 1), @{$master->{'choices'}});
+
+    # Check for extra entries (write-ins should be supported in the future)
+    for (keys %h) {
+        push @extra, $_ unless exists $mh{$_};
+    }
+
+    # Check for duplicate entries
+    @dups = grep { $h{$_} > 1 } keys %h;
+
+    # Check for missing entries (not necessarily an error)
+    for (keys %mh) {
+        push @missing, $_ unless exists $h{$_};
+    }
+
+    # Report errors and warnings
+    if (@extra) {
+        if ($Votify::mode eq 'user') {
+            print <<EOT;
+Your ballot has some extra entries that are not part of this election.  Sorry,
+but write-ins are not (yet) supported.  Please remove these from your ballot:
+
+EOT
+            print map "\t$_\n", @extra;
+            print "\n";
+        }
+        $errors_found++;
+    }
+    if (@dups) {
+        if ($Votify::mode eq 'user') {
+            print <<EOT;
+Your ballot has some duplicate entries.  Please resolve these to a single entry
+to avoid ambiguities:
+
+EOT
+            print map "\t$_\n", @dups;
+            print "\n";
+        }
+        $errors_found++;
+    }
+    if (@{$self->{'choices'}} == 0) {
+        if ($Votify::mode eq 'user') {
+            print <<EOT;
+Your ballot doesn't contain any entries.  You can start over by first removing
+the existing ballot, then using --new to generate a new ballot.  See --help for
+more information.
+
+EOT
+        }
+        $errors_found++;
+    }
+    elsif (@missing and $Votify::mode eq 'user') {
+        print <<EOT;
+Your ballot is missing some entries.  This is not an error, but note that these
+will be implied as a final line, with no preference between them, like this:
+
+EOT
+        print "\t", join(" ", @missing), "\n";
+        print "\n";
+    }
+    if ($Votify::mode eq 'user' and !$errors_found and
+        @{$self->{'choices'}} == 1 and
+        scalar(keys %h) == scalar(keys %mh))
+    {
+        print <<EOT;
+Your ballot contains all the candidates on a single line!  This means you have
+no preference between the candidates.  This is not an error, but note that this
+is a meaningless ballot that will have no effect on the election.
+
+EOT
+    }
+
+    # Stop if there were errors
+    if ($Votify::mode eq 'user' and $errors_found) {
+        print("There were errors found in your ballot.\n");
+        die("Please correct them and try again.\n\n");
+    }
+    return $errors_found;
+}
+
+sub to_s {
+    my ($self) = @_;
+    join '', map "@$_\n", @{$self->{'choices'}};
+}
+
+1;
+
+__END__
+
+$Log: Votify.pm,v $
+Revision 1.5  2005/05/16 23:58:09  agriffis
+change wording
+
+Revision 1.4  2005/05/16 18:40:07  agriffis
+fix shortname calculation
+
+Revision 1.3  2005/05/16 18:10:46  agriffis
+ranking works completely now, even if it needs badly to be refactored
+
+Revision 1.2  2005/05/16 04:03:46  agriffis
+add first pass at countify --rank
+
+
+__END__
+
+$Log: Votify.pm,v $
+Revision 1.5  2005/05/16 23:58:09  agriffis
+change wording
+
+Revision 1.4  2005/05/16 18:40:07  agriffis
+fix shortname calculation
+
+Revision 1.3  2005/05/16 18:10:46  agriffis
+ranking works completely now, even if it needs badly to be refactored
+
+Revision 1.2  2005/05/16 04:03:46  agriffis
+add first pass at countify --rank
+
+Revision 1.3  2005/05/09 23:12:02  agriffis
+Add support for registered voters
+
+Revision 1.2  2005/05/05 23:03:46  agriffis
+Fix indentation (and some output as well)
+
+Revision 1.1  2005/05/05 22:05:34  agriffis
+first pass at Gentoo Foundation voting program
+
+# vim:sw=4 et

diff --git a/trustees-201306/ballot-trustees-201306 b/trustees-201306/ballot-trustees-201306
new file mode 100644
index 0000000..e69de29

diff --git a/trustees-201306/election-details b/trustees-201306/election-details
new file mode 100644
index 0000000..2eb59cb
--- /dev/null
+++ b/trustees-201306/election-details
@@ -0,0 +1,7 @@
+name: trustees-201306
+startDate: 2013-07-14 00:00:00 UTC
+endDate: 2013-08-11 00:00:01 UTC
+officials: jmbsvicetto, neddyseagoon, robbat2, tampakrap
+voters: http://www.gentoo.org/proj/en/elections/trustees/2013/voters-trustees-201306.txt
+ballot: http://www.gentoo.org/proj/en/elections/trustees/2013/ballot-trustees-201306.txt
+

diff --git a/trustees-201306/officials-trustees-201306 b/trustees-201306/officials-trustees-201306
new file mode 100644
index 0000000..38f9b76
--- /dev/null
+++ b/trustees-201306/officials-trustees-201306
@@ -0,0 +1,4 @@
+jmbsvicetto
+neddyseagoon
+robbat2
+tampakrap


                 reply	other threads:[~2013-06-28 12:52 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1372423759.76c33523cfa735aa70a42a3913a4878db4fa57af.jmbsvicetto@gentoo \
    --to=jmbsvicetto@gentoo.org \
    --cc=gentoo-commits@lists.gentoo.org \
    --cc=gentoo-dev@lists.gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox