1 |
commit: 8e9679101b638f65af8be2e034263efed09bf22e |
2 |
Author: Robin H. Johnson <robbat2 <AT> gentoo <DOT> org> |
3 |
AuthorDate: Fri Jun 18 06:56:42 2021 +0000 |
4 |
Commit: Robin H. Johnson <robbat2 <AT> gentoo <DOT> org> |
5 |
CommitDate: Fri Jun 18 06:56:42 2021 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/elections.git/commit/?id=8e967910 |
7 |
|
8 |
Votify: improvements from re-using the codebase for another project |
9 |
|
10 |
Signed-off-by: Robin H. Johnson <robbat2 <AT> gentoo.org> |
11 |
|
12 |
Votify.pm | 49 +++++++++++++++++++++++++++++++++++-------------- |
13 |
countify | 19 +++++++++++++------ |
14 |
2 files changed, 48 insertions(+), 20 deletions(-) |
15 |
|
16 |
diff --git a/Votify.pm b/Votify.pm |
17 |
index 87fba2f..35a717f 100644 |
18 |
--- a/Votify.pm |
19 |
+++ b/Votify.pm |
20 |
@@ -8,13 +8,13 @@ |
21 |
|
22 |
package Votify; |
23 |
|
24 |
-use POSIX; |
25 |
+use Carp::Always; |
26 |
use Cwd qw(abs_path); |
27 |
+use Data::Dumper; |
28 |
use File::Basename; |
29 |
use File::Spec::Functions; |
30 |
use List::Util; |
31 |
-use Data::Dumper; |
32 |
-use Carp::Always; |
33 |
+use POSIX; |
34 |
use strict; |
35 |
use warnings; |
36 |
|
37 |
@@ -218,8 +218,8 @@ sub new { |
38 |
close(F); |
39 |
|
40 |
# assign confirmation numbers randomly |
41 |
- for my $v (@voterlist) { |
42 |
- do { $r = int rand 0xffff } while exists $self->{'voters'}{$r}; |
43 |
+ for my $v (List::Util::shuffle(@voterlist)) { |
44 |
+ do { $r = int rand 0xffffffff } while exists $self->{'voters'}{$r}; |
45 |
$self->{'voters'}{$r} = $v; |
46 |
$self->{'confs'}{$v} = $r; |
47 |
} |
48 |
@@ -239,7 +239,7 @@ sub confs { |
49 |
|
50 |
sub voters { |
51 |
my ($self) = @_; |
52 |
- sort keys %{$self->{'confs'}}; |
53 |
+ return sort keys %{$self->{'confs'}}; |
54 |
} |
55 |
|
56 |
sub getvoter { |
57 |
@@ -252,7 +252,7 @@ sub getconf { |
58 |
return $self->{'confs'}{$voter}; |
59 |
} |
60 |
|
61 |
-sub write { |
62 |
+sub write_confs { |
63 |
my ($self, $filename) = @_; |
64 |
|
65 |
$filename ||= $self->{'default_filename'}; |
66 |
@@ -263,8 +263,8 @@ sub write { |
67 |
} |
68 |
|
69 |
open(F, ">$filename") or die("can't write to $filename"); |
70 |
- for my $c ($self->confs) { |
71 |
- printf F "%04x %s\n", $c, $self->getvoter($c); |
72 |
+ for my $c (sort { $a <=> $b } map { int $_ } $self->confs) { |
73 |
+ printf F "%08x %s\n", $c, $self->getvoter($c); |
74 |
} |
75 |
close F; |
76 |
} |
77 |
@@ -287,6 +287,7 @@ sub new { |
78 |
default_filename => catfile($datadir, "master-$election_name"), |
79 |
filename => '', |
80 |
voterlist => $vl, |
81 |
+ casting_voters => {}, # indexed by voter |
82 |
ballots => {}, # indexed by conf num |
83 |
candidates => undef, # indexed by long name |
84 |
table => undef, # indexed by row+column |
85 |
@@ -326,6 +327,7 @@ sub collect { |
86 |
next; |
87 |
} |
88 |
$self->{'ballots'}{$c} = $b; |
89 |
+ $self->{'casting_voters'}{$v} = 1; |
90 |
} |
91 |
elsif (-f "$home/.ballot-$self->{election}") { |
92 |
print STDERR "Warning: $v did not submit their ballot\n"; |
93 |
@@ -333,7 +335,7 @@ sub collect { |
94 |
} |
95 |
} |
96 |
|
97 |
-sub write { |
98 |
+sub write_master { |
99 |
my ($self, $filename) = @_; |
100 |
|
101 |
$filename ||= $self->{'default_filename'}; |
102 |
@@ -344,14 +346,15 @@ sub write { |
103 |
} |
104 |
|
105 |
open(F, ">$filename") or die("can't write to $filename"); |
106 |
- for my $c (sort keys %{$self->{'ballots'}}) { |
107 |
- printf F "--------- confirmation %04x ---------\n", $c; |
108 |
+ for my $c (sort { $a <=> $b } map { int $_ } keys %{$self->{'ballots'}}) { |
109 |
+ my $confid = sprintf("%08x",$c); |
110 |
+ printf F "--------- confirmation %s ---------\n", $confid; |
111 |
print F $self->{'ballots'}{$c}->to_s |
112 |
} |
113 |
close F; |
114 |
} |
115 |
|
116 |
-sub read { |
117 |
+sub read_master { |
118 |
my ($self, $filename) = @_; |
119 |
my ($election, $entries) = $self->{'election'}; |
120 |
|
121 |
@@ -362,7 +365,7 @@ sub read { |
122 |
{ local $/ = undef; $entries = <F>; } |
123 |
for my $e (split /^--------- confirmation /m, $entries) { |
124 |
next unless $e; # skip the first zero-length record |
125 |
- unless ($e =~ /^([[:xdigit:]]{4}) ---------\n(.*)$/s) { |
126 |
+ unless ($e =~ /^([[:xdigit:]]{4,12}) ---------\n(.*)$/s) { |
127 |
die "error parsing entry:\n$e"; |
128 |
} |
129 |
my ($c, $s, $b) = ($1, $2, Ballot->new($election)); |
130 |
@@ -371,6 +374,24 @@ sub read { |
131 |
} |
132 |
} |
133 |
|
134 |
+sub write_casting_voters { |
135 |
+ my ($self, $filename) = @_; |
136 |
+ |
137 |
+ $filename ||= $self->{'default_filename'}; |
138 |
+ $self->{'filename'} = $filename; |
139 |
+ |
140 |
+ if (-f $filename) { |
141 |
+ die "$filename already exists; please remove it first"; |
142 |
+ } |
143 |
+ |
144 |
+ open(F, ">$filename") or die("can't write to $filename"); |
145 |
+ for my $v (sort keys %{$self->{'casting_voters'}}) { |
146 |
+ printf F "%s\n", $v; |
147 |
+ } |
148 |
+ close F; |
149 |
+} |
150 |
+ |
151 |
+ |
152 |
sub generate_candidates { |
153 |
my ($self) = @_; |
154 |
my ($B, @C, $s); |
155 |
|
156 |
diff --git a/countify b/countify |
157 |
index 13618a4..cf31d69 100755 |
158 |
--- a/countify |
159 |
+++ b/countify |
160 |
@@ -19,9 +19,13 @@ BEGIN { |
161 |
push @INC, $dirname; |
162 |
} |
163 |
|
164 |
-use POSIX; |
165 |
+use Cwd qw(abs_path); |
166 |
+use File::Basename; |
167 |
+use File::Spec::Functions; |
168 |
use Getopt::Long; |
169 |
use List::Util; |
170 |
+use POSIX; |
171 |
+ |
172 |
use Votify 'official'; |
173 |
use strict; |
174 |
|
175 |
@@ -94,12 +98,15 @@ if ($opt{'collect'}) { |
176 |
$master->collect($vl->voters); |
177 |
for my $o ($ol->officials) { |
178 |
my ($uid, $home) = (getpwnam $o)[2,7]; |
179 |
- mkdir "$home/results-$election"; |
180 |
- $master->write("$home/results-$election/master-$election"); |
181 |
- $vl->write("$home/results-$election/confs-$election"); |
182 |
+ $home = "/home/$o" unless defined $home; |
183 |
+ mkdir catfile("$home", "results-$election"); |
184 |
+ $master->write_master("$home/results-$election/master-$election"); |
185 |
+ $master->write_casting_voters("$home/results-$election/casting-voters-$election"); |
186 |
+ $vl->write_confs("$home/results-$election/confs-$election"); |
187 |
chown $uid, -1, "$home/results-$election", |
188 |
"$home/results-$election/master-$election", |
189 |
- "$home/results-$election/confs-$election"; |
190 |
+ "$home/results-$election/confs-$election", |
191 |
+ "$home/results-$election/casting-voters-$election"; |
192 |
} |
193 |
exit 0; |
194 |
} |
195 |
@@ -107,7 +114,7 @@ if ($opt{'collect'}) { |
196 |
if ($opt{'rank'}) { |
197 |
my ($master) = MasterBallot->new($election, $vl); |
198 |
my (@candidates, @winner, @ranked, @ranks); |
199 |
- $master->read("$ENV{HOME}/results-$election/master-$election"); |
200 |
+ $master->read_master("$ENV{HOME}/results-$election/master-$election"); |
201 |
$master->generate_candidates(); |
202 |
@candidates = sort keys %{$master->{'candidates'}}; |