commit:     8e9679101b638f65af8be2e034263efed09bf22e
Author:     Robin H. Johnson <robbat2 <AT> gentoo <DOT> org>
AuthorDate: Fri Jun 18 06:56:42 2021 +0000
Commit:     Robin H. Johnson <robbat2 <AT> gentoo <DOT> org>
CommitDate: Fri Jun 18 06:56:42 2021 +0000
URL:        https://gitweb.gentoo.org/proj/elections.git/commit/?id=8e967910

Votify: improvements from re-using the codebase for another project

Signed-off-by: Robin H. Johnson <robbat2 <AT> gentoo.org>

 Votify.pm | 49 +++++++++++++++++++++++++++++++++++--------------
 countify  | 19 +++++++++++++------
 2 files changed, 48 insertions(+), 20 deletions(-)

diff --git a/Votify.pm b/Votify.pm
index 87fba2f..35a717f 100644
--- a/Votify.pm
+++ b/Votify.pm
@@ -8,13 +8,13 @@
 
 package Votify;
 
-use POSIX;
+use Carp::Always;
 use Cwd qw(abs_path);
+use Data::Dumper;
 use File::Basename;
 use File::Spec::Functions;
 use List::Util;
-use Data::Dumper;
-use Carp::Always;
+use POSIX;
 use strict;
 use warnings;
 
@@ -218,8 +218,8 @@ sub new {
     close(F);
 
     # assign confirmation numbers randomly
-    for my $v (@voterlist) {
-        do { $r = int rand 0xffff } while exists $self->{'voters'}{$r};
+    for my $v (List::Util::shuffle(@voterlist)) {
+        do { $r = int rand 0xffffffff } while exists $self->{'voters'}{$r};
         $self->{'voters'}{$r} = $v;
         $self->{'confs'}{$v} = $r;
     }
@@ -239,7 +239,7 @@ sub confs {
 
 sub voters {
     my ($self) = @_;
-    sort keys %{$self->{'confs'}};
+    return sort keys %{$self->{'confs'}};
 }
 
 sub getvoter {
@@ -252,7 +252,7 @@ sub getconf {
     return $self->{'confs'}{$voter};
 }
 
-sub write {
+sub write_confs {
     my ($self, $filename) = @_;
 
     $filename ||= $self->{'default_filename'};
@@ -263,8 +263,8 @@ sub write {
     }
 
     open(F, ">$filename") or die("can't write to $filename");
-    for my $c ($self->confs) {
-        printf F "%04x %s\n", $c, $self->getvoter($c);
+    for my $c (sort { $a <=> $b } map { int $_ } $self->confs) {
+        printf F "%08x %s\n", $c, $self->getvoter($c);
     }
     close F;
 }
@@ -287,6 +287,7 @@ sub new {
         default_filename => catfile($datadir, "master-$election_name"),
         filename => '',
         voterlist => $vl,
+        casting_voters => {},   # indexed by voter
         ballots => {},          # indexed by conf num
         candidates => undef,    # indexed by long name
         table => undef,         # indexed by row+column
@@ -326,6 +327,7 @@ sub collect {
                 next;
             }
             $self->{'ballots'}{$c} = $b;
+            $self->{'casting_voters'}{$v} = 1;
         }
         elsif (-f "$home/.ballot-$self->{election}") {
             print STDERR "Warning: $v did not submit their ballot\n";
@@ -333,7 +335,7 @@ sub collect {
     }
 }
 
-sub write {
+sub write_master {
     my ($self, $filename) = @_;
 
     $filename ||= $self->{'default_filename'};
@@ -344,14 +346,15 @@ sub write {
     }
 
     open(F, ">$filename") or die("can't write to $filename");
-    for my $c (sort keys %{$self->{'ballots'}}) {
-        printf F "--------- confirmation %04x ---------\n", $c;
+    for my $c (sort { $a <=> $b } map { int $_ } keys %{$self->{'ballots'}}) {
+        my $confid = sprintf("%08x",$c);
+        printf F "--------- confirmation %s ---------\n", $confid;
         print F $self->{'ballots'}{$c}->to_s
     }
     close F;
 }
 
-sub read {
+sub read_master {
     my ($self, $filename) = @_;
     my ($election, $entries) = $self->{'election'};
 
@@ -362,7 +365,7 @@ sub read {
     { 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) {
+        unless ($e =~ /^([[:xdigit:]]{4,12}) ---------\n(.*)$/s) {
             die "error parsing entry:\n$e";
         }
         my ($c, $s, $b) = ($1, $2, Ballot->new($election));
@@ -371,6 +374,24 @@ sub read {
     }
 }
 
+sub write_casting_voters {
+    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 $v (sort keys %{$self->{'casting_voters'}}) {
+        printf F "%s\n", $v;
+    }
+    close F;
+}
+
+
 sub generate_candidates {
     my ($self) = @_;
     my ($B, @C, $s);

diff --git a/countify b/countify
index 13618a4..cf31d69 100755
--- a/countify
+++ b/countify
@@ -19,9 +19,13 @@ BEGIN {
     push @INC, $dirname;
 }
 
-use POSIX;
+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;
 
@@ -94,12 +98,15 @@ if ($opt{'collect'}) {
     $master->collect($vl->voters);
     for my $o ($ol->officials) {
         my ($uid, $home) = (getpwnam $o)[2,7];
-        mkdir "$home/results-$election";
-        $master->write("$home/results-$election/master-$election");
-        $vl->write("$home/results-$election/confs-$election");
+        $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/confs-$election",
+            "$home/results-$election/casting-voters-$election";
     }
     exit 0;
 }
@@ -107,7 +114,7 @@ if ($opt{'collect'}) {
 if ($opt{'rank'}) {
     my ($master) = MasterBallot->new($election, $vl);
     my (@candidates, @winner, @ranked, @ranks);
-    $master->read("$ENV{HOME}/results-$election/master-$election");
+    $master->read_master("$ENV{HOME}/results-$election/master-$election");
     $master->generate_candidates();
     @candidates = sort keys %{$master->{'candidates'}};
 

Reply via email to