fu: Close-up of Fu, bringing a scoop of water to her mouth (Default)
fu ([personal profile] fu) wrote in [site community profile] changelog2010-09-07 07:28 am

[dw-free] Anonymized Detailed Results as a poll option

[commit: http://hg.dwscoalition.org/dw-free/rev/4fcdfd9c7c1c]

http://bugs.dwscoalition.org/show_bug.cgi?id=2363

Allow poll results to be anonymized, so that no one can see the username
that cast each vote. Instead, an automatically generated identifier will be
used (so that it's possible to identify the votes that were cast per set)

Patch by [personal profile] jportela.

Files modified:
  • bin/upgrading/en.dat
  • bin/upgrading/update-db-general.pl
  • cgi-bin/LJ/Event/PollVote.pm
  • cgi-bin/LJ/Poll.pm
  • cgi-bin/LJ/Poll/Question.pm
  • htdocs/poll/create.bml
  • htdocs/poll/create.bml.text
  • htdocs/tools/endpoints/poll.bml
--------------------------------------------------------------------------------
diff -r 7f28d1bf7c09 -r 4fcdfd9c7c1c bin/upgrading/en.dat
--- a/bin/upgrading/en.dat	Tue Sep 07 11:19:36 2010 +0800
+++ b/bin/upgrading/en.dat	Tue Sep 07 15:28:41 2010 +0800
@@ -2381,6 +2381,8 @@ poll.error.whoview=whoview must be 'all'
 
 poll.error.whovote=whovote must be 'all' or 'friends'.
 
+poll.isanonymous=This poll is anonymous.
+
 poll.isclosed=This poll is closed.
 
 poll.participants=, participants: [[total]]
diff -r 7f28d1bf7c09 -r 4fcdfd9c7c1c bin/upgrading/update-db-general.pl
--- a/bin/upgrading/update-db-general.pl	Tue Sep 07 11:19:36 2010 +0800
+++ b/bin/upgrading/update-db-general.pl	Tue Sep 07 15:28:41 2010 +0800
@@ -3889,6 +3889,11 @@ EOF
          do_alter( 'moods',
                     q{ALTER TABLE moods ADD COLUMN weight tinyint unsigned default NULL} );
      }
+
+     unless ( column_type( 'poll2', 'isanon' ) ) {
+         do_alter( 'poll2',
+                   "ALTER TABLE poll2 ADD COLUMN isanon enum('yes','no') NOT NULL default 'no'");
+     }
 });
 
 
diff -r 7f28d1bf7c09 -r 4fcdfd9c7c1c cgi-bin/LJ/Event/PollVote.pm
--- a/cgi-bin/LJ/Event/PollVote.pm	Tue Sep 07 11:19:36 2010 +0800
+++ b/cgi-bin/LJ/Event/PollVote.pm	Tue Sep 07 15:28:41 2010 +0800
@@ -68,21 +68,23 @@ sub pollname {
 
 sub as_string {
     my $self = shift;
+    
+    my $voter = ($self->poll->isanon eq "yes") ? "Anonymous user" : $self->voter->display_username;
     return sprintf("%s has voted in %s at %s",
-                   $self->voter->display_username, $self->pollname, $self->entry->url);
+                   $voter, $self->pollname, $self->entry->url);
 }
 
 sub as_html {
     my $self = shift;
-    my $voter = $self->voter;
+    my $voter = ($self->poll->isanon eq "yes") ? "Anonymous user" : $self->voter->ljuser_display;
     my $poll = $self->poll;
 
-    return sprintf("%s has voted in a deleted poll", $voter->ljuser_display)
+    return sprintf("%s has voted in a deleted poll", $voter)
         unless $poll && $poll->valid;
 
     my $entry = $self->entry;
     return sprintf("%s has voted <a href='%s'>in %s</a>",
-                   $voter->ljuser_display, $entry->url, $self->pollname);
+                   $voter, $entry->url, $self->pollname);
 }
 
 sub as_html_actions {
@@ -122,10 +124,11 @@ sub as_email_subject {
 
 sub _as_email {
     my ($self, $u, $is_html) = @_;
+    my $voter = $is_html ? ($self->voter->ljuser_display) : ($self->voter->display_username);
 
     my $vars = {
         user     => $is_html ? ($u->ljuser_display) : ($u->display_username),
-        voter    => $is_html ? ($self->voter->ljuser_display) : ($self->voter->display_username),
+        voter    => ($self->poll->isanon eq "yes") ? "Anonymous user" : $voter,
         pollname => $self->pollname,
     };
 
diff -r 7f28d1bf7c09 -r 4fcdfd9c7c1c cgi-bin/LJ/Poll.pm
--- a/cgi-bin/LJ/Poll.pm	Tue Sep 07 11:19:36 2010 +0800
+++ b/cgi-bin/LJ/Poll.pm	Tue Sep 07 15:28:41 2010 +0800
@@ -28,9 +28,9 @@ sub _memcache_stored_props          {
 sub _memcache_stored_props          {
     # first element of props is a VERSION
     # next - allowed object properties
-    return qw/ 1
+    return qw/ 2
                ditemid itemid
-               pollid journalid posterid whovote whoview name status questions props
+               pollid journalid posterid isanon whovote whoview name status questions props
                /;
 }
     *_memcache_hashref_to_object    = \*absorb_row;
@@ -78,7 +78,8 @@ sub create {
         $journalid = $opts{journalid} or croak "No journalid";
         $posterid  = $opts{posterid} or croak "No posterid";
     }
-
+    
+    my $isanon = $opts{isanon} or croak "No isanon";
     my $whovote = $opts{whovote} or croak "No whovote";
     my $whoview = $opts{whoview} or croak "No whoview";
     my $name    = $opts{name} || '';
@@ -98,9 +99,9 @@ sub create {
 
     my $dbh = LJ::get_db_writer();
 
-    $u->do( "INSERT INTO poll2 (journalid, pollid, posterid, whovote, whoview, name, ditemid) " .
-            "VALUES (?, ?, ?, ?, ?, ?, ?)", undef,
-            $journalid, $pollid, $posterid, $whovote, $whoview, $name, $ditemid );
+    $u->do( "INSERT INTO poll2 (journalid, pollid, posterid, isanon, whovote, whoview, name, ditemid) " .
+            "VALUES (?, ?, ?, ?, ?, ?, ?, ?)", undef,
+            $journalid, $pollid, $posterid, $isanon, $whovote, $whoview, $name, $ditemid );
     die $u->errstr if $u->err;
 
     # made poll, insert global pollid->journalid mapping into global pollowner map
@@ -252,6 +253,7 @@ sub new_from_html {
                 $popts{'questions'} = [];
 
                 $popts{'name'} = $opts->{'name'};
+                $popts{'isanon'} = $opts->{'isanon'} || "no";
                 $popts{'whovote'} = lc($opts->{'whovote'}) || "all";
                 $popts{'whoview'} = lc($opts->{'whoview'}) || "all";
 
@@ -267,7 +269,9 @@ sub new_from_html {
                     $popts{props}->{createdate} = $opts->{createdate} || undef;
                 }
                 LJ::Hooks::run_hook('get_more_options_from_poll', finalopts => \%popts, givenopts => $opts, journalu => $journal);
-
+                
+                $popts{'isanon'} = "no" unless ($popts{'isanon'} eq "yes");
+                
                 if ($popts{'whovote'} ne "all" &&
                     $popts{'whovote'} ne "trusted")
                 {
@@ -529,7 +533,7 @@ sub save_to_db {
     $createopts{name} = $opts{name} || $self->{name};
     $createopts{props} = $opts{props} || $self->{props};
 
-    foreach my $f (qw(ditemid journalid posterid questions whovote whoview)) {
+    foreach my $f (qw(ditemid journalid posterid questions isanon whovote whoview)) {
         $createopts{$f} = $opts{$f} || $self->{$f} or croak "Field $f required for save_to_db";
     }
 
@@ -567,7 +571,7 @@ sub _load {
         or die "Invalid journalid $journalid";
 
     $row = $u->selectrow_hashref( "SELECT pollid, journalid, ditemid, " .
-                                  "posterid, whovote, whoview, name, status " .
+                                  "posterid, isanon, whovote, whoview, name, status " .
                                   "FROM poll2 WHERE pollid=? " .
                                   "AND journalid=?", undef, $self->pollid, $journalid );
     die $u->errstr if $u->err;
@@ -590,7 +594,7 @@ sub absorb_row {
 
     # questions is an optional field for creating a fake poll object for previewing
     $self->{ditemid} = $row->{ditemid} || $row->{itemid}; # renamed to ditemid in poll2
-    $self->{$_} = $row->{$_} foreach qw(pollid journalid posterid whovote whoview name status questions props);
+    $self->{$_} = $row->{$_} foreach qw(pollid journalid posterid isanon whovote whoview name status questions props);
     $self->{_loaded} = 1;
     return $self;
 }
@@ -652,6 +656,11 @@ sub name {
     my $self = shift;
     $self->_load;
     return $self->{name};
+}
+sub isanon {
+    my $self = shift;
+    $self->_load;
+    return $self->{isanon};
 }
 sub whovote {
     my $self = shift;
@@ -774,6 +783,9 @@ sub preview {
 
     $ret .= "<br />\n";
 
+    $ret .= "This poll is <b>anonymous</b><br/>\n"
+        if ($self->isanon eq "yes");
+
     my $whoview = $self->whoview eq "none" ? "none_remote" : $self->whoview;
     $ret .= LJ::Lang::ml('poll.security2', { 'whovote' => LJ::Lang::ml('poll.security.'.$self->whovote), 'whoview' => LJ::Lang::ml('poll.security.'.$whoview), });
 
@@ -857,7 +869,7 @@ sub render {
         my $text = $q->text;
         LJ::Poll->clean_poll(\$text);
         $ret .= $text;
-        $ret .= '<div>' . $q->answers_as_html($self->journalid, $page, $pagesize) . '</div>';
+        $ret .= '<div>' . $q->answers_as_html($self->journalid, $self->isanon, $page, $pagesize) . '</div>';
         return $ret;
     }
 
@@ -897,6 +909,9 @@ sub render {
     $ret .= "<span style='font-family: monospace; font-weight: bold; font-size: 1.2em;'>" .
             LJ::Lang::ml( 'poll.isclosed' ) . "</span><br />\n"
         if ($self->is_closed);
+    
+    $ret .= "This poll is <b>anonymous</b><br/>\n"
+        if ($self->isanon eq "yes");
 
     my $whoview = $self->whoview;
     if ($whoview eq "none") {
diff -r 7f28d1bf7c09 -r 4fcdfd9c7c1c cgi-bin/LJ/Poll/Question.pm
--- a/cgi-bin/LJ/Poll/Question.pm	Tue Sep 07 11:19:36 2010 +0800
+++ b/cgi-bin/LJ/Poll/Question.pm	Tue Sep 07 15:28:41 2010 +0800
@@ -219,6 +219,7 @@ sub answers_as_html {
 sub answers_as_html {
     my $self = shift;
     my $jid = shift;
+    my $isanon = shift;
 
     my $page     =  shift || 1;
     my $pagesize =  shift || 2000;
@@ -236,6 +237,7 @@ sub answers_as_html {
             "WHERE pr.pollid=? AND pollqid=? " .
             "AND ps.pollid=pr.pollid AND ps.userid=pr.userid " .
             "AND ps.journalid=? ".
+            "ORDER BY ps.datesubmit " .
             "LIMIT $LIMIT" );
     $sth->execute( $self->pollid, $self->pollqid, $jid );
     die $sth->errstr if $sth->err;
@@ -245,7 +247,8 @@ sub answers_as_html {
     my @res;
     push @res, $_ while $_ = $sth->fetchrow_hashref;
     @res = sort { $a->{datesubmit} cmp $b->{datesubmit} } @res;
-
+    
+    my $user_i = 0;    #incrementer for user anonymous ids
     foreach my $res (@res) {
         my ($userid, $value) = ($res->{userid}, $res->{value}, $res->{pollqid});
         my @items = $self->items;
@@ -263,7 +266,9 @@ sub answers_as_html {
         }
 
         LJ::Poll->clean_poll(\$value);
-        $ret .= "<div>" . $u->ljuser_display . " -- $value</div>\n";
+        my $user_display = $isanon eq "yes" ? "User <b>#" . ++$user_i . "</b>" : $u->ljuser_display;
+
+        $ret .= "<div>" . $user_display . " -- $value</div>\n";
     }
 
     return $ret;
diff -r 7f28d1bf7c09 -r 4fcdfd9c7c1c htdocs/poll/create.bml
--- a/htdocs/poll/create.bml	Tue Sep 07 11:19:36 2010 +0800
+++ b/htdocs/poll/create.bml	Tue Sep 07 15:28:41 2010 +0800
@@ -146,6 +146,7 @@ _c?>
         my $poll = {
             "name" => "",
             "count" => "0",
+            "isanon" => "no",
             "whoview" => "all",
             "whovote" => "all",
             "pq" => [],
@@ -157,7 +158,7 @@ _c?>
             if $POST{'count'} > $RULES{'elements'}->{'max'};
 
         # form properties
-        foreach my $it (qw(count name whoview whovote)) {
+        foreach my $it (qw(count name isanon whoview whovote)) {
             $poll->{$it} = $POST{$it} if $POST{$it};
         }
 
@@ -403,11 +404,20 @@ _c?>
         $ret .= "<div style='margin: 10px 0 20px 40px'><b>$ML{'.haserrors'}</b></div>\n"
             if %$err;
 
-        ### Poll Properties -- name, whovote, whoview
+        ### Poll Properties -- name, isanon, whovote, whoview
 
         $ret .= "<?h1 $ML{'.properties'} h1?>\n";
 
         $ret .= "<div style='margin-left: 40px; margin-bottom: 20px'>\n";
+        
+        $ret .= "<p>" . LJ::html_check({
+            name => 'isanon',
+            id => 'isanon',
+            label => $ML{'.isanon'}, 
+            value => "yes",
+            selected => ( $poll->{isanon} eq "yes" )
+        }) . "</p>\n";
+        
         $ret .= "<p>$ML{'.whoview2'}<br /><select name='whoview'>\n";
         foreach my $sec ( qw(all trusted none) ) {
             $ret .= "<option value='$sec'";
@@ -576,7 +586,7 @@ _c?>
             my $ret;
 
             # start out the tag
-            $ret .= "<poll name='" . LJ::ehtml($poll->{'name'}) . "' whovote='" . LJ::ehtml($poll->{'whovote'}) . "' whoview='" . LJ::ehtml($poll->{'whoview'}) . "'>\n";
+            $ret .= "<poll name='" . LJ::ehtml($poll->{'name'}) . "' isanon='" . $poll->{'isanon'} . "' whovote='" . LJ::ehtml($poll->{'whovote'}) . "' whoview='" . LJ::ehtml($poll->{'whoview'}) . "'>\n";
 
             # go through and make <poll-question> tags
             foreach my $q (0..$poll->{'count'}-1) {
diff -r 7f28d1bf7c09 -r 4fcdfd9c7c1c htdocs/poll/create.bml.text
--- a/htdocs/poll/create.bml.text	Tue Sep 07 11:19:36 2010 +0800
+++ b/htdocs/poll/create.bml.text	Tue Sep 07 15:28:41 2010 +0800
@@ -39,6 +39,8 @@
 
 .insertquestion=Insert question here:
 
+.isanon=Anonymize poll results. No one can see who voted, not even you.
+
 .options=Options:
 
 .options.limitreached=Option limit reached
diff -r 7f28d1bf7c09 -r 4fcdfd9c7c1c htdocs/tools/endpoints/poll.bml
--- a/htdocs/tools/endpoints/poll.bml	Tue Sep 07 11:19:36 2010 +0800
+++ b/htdocs/tools/endpoints/poll.bml	Tue Sep 07 15:28:41 2010 +0800
@@ -49,7 +49,7 @@ _c?>
         my $question = $poll->question($pollqid) or return $err->("Error loading question $pollqid");
         my $pages    = $question->answers_pages($poll->journalid, $pagesize);
         $ret->{paging_html} = $question->paging_bar_as_html($page, $pages, $pagesize, $poll->journalid, $pollid, $pollqid);
-        $ret->{answer_html} = $question->answers_as_html($poll->journalid, $page, $pagesize, $pages);
+        $ret->{answer_html} = $question->answers_as_html($poll->journalid, $poll->isanon, $page, $pagesize, $pages);
     } else {
         return $err->("Invalid action $action");
     }
--------------------------------------------------------------------------------
yvi: Kaylee half-smiling, looking very pretty (Default)

[personal profile] yvi 2010-09-07 08:50 am (UTC)(link)
Also liking this a lot!
gchick: Small furry animal wearing a tin-foil hat (Default)

[personal profile] gchick 2010-09-07 11:40 am (UTC)(link)
Yippee!
turlough: castle on mountain top in winter, Burg Hohenzollern ((dw) creative dreams)

[personal profile] turlough 2010-09-07 02:55 pm (UTC)(link)
Interesting.