OSDN Git Service

Model::Metamoderations: implement create method and add related internal methods
authorhylom <hylom@users.sourceforge.jp>
Fri, 27 Jan 2017 14:24:49 +0000 (23:24 +0900)
committerhylom <hylom@users.sourceforge.jp>
Fri, 27 Jan 2017 14:24:49 +0000 (23:24 +0900)
src/newslash_web/lib/Newslash/Model/Metamoderations.pm

index 103ef40..3638e62 100644 (file)
@@ -2,12 +2,346 @@ package Newslash::Model::Metamoderations;
 use Newslash::Model::Base -base;
 
 use Data::Dumper;
+use List::Util qw(reduce any);
+use POSIX;
 
 my $m2_params = {
                  m2_mintokes => 0,
                  m2_consensus => 9,
+
+                 count_threshold => 3,
+                 agreement_threshold_ratio => 0.66,
+                 prevent_params => {
+                                    alpha => 0.85,
+                                    beta => 0.43,
+                                    min => 0.5,
+                                    max => 1.0,
+                                    min_count => 3,
+                                    max_count => 20,
+                                    reduction => 1.0,
+                                   },
+                 enforce_params => {
+                                    alpha => 0.85,
+                                    beta => 0.086,
+                                    min => 0.05,
+                                    max => 0.15,
+                                    min_count => 3,
+                                    max_count => 20,
+                                    reduction => 0.1,
+                                   },
+                 token_unfair_params => {
+                                       alpha => 0.56,
+                                       beta => 21,
+                                       min => 1.0,
+                                       max => 20.0,
+                                       min_count => 3,
+                                       max_count => 20,
+                                      },
+                 token_fair_params => {
+                                       alpha => 0.56,
+                                       beta => 4.2,
+                                       min => 0.2,
+                                       max => 4.0,
+                                       min_count => 3,
+                                       max_count => 20,
+                                      },
                 };
 
+sub create {
+    my ($self, $user, $moderation, $value) = @_;
+    my $moderations = $self->new_instance_of("Moderations");
+
+    # user validation
+    if (!$moderations->is_metamoderatable($user, $moderation)
+        && !$user->{is_admin}
+        && !$user->{editor} ) {
+        $self->set_error("you cannot metamoderate the moderation");
+        return;
+    }
+
+    my $metamods = $self->select(id => $moderation->{id});
+    return if !defined $metamods;
+    my $mods = $moderations->select(cid => $moderation->{cid});
+    return if !defined $mods;
+
+    # calc unchanged moderation's fixed scores
+    my $unchanged_scores = [];
+    for my $mod (@$mods) {
+        next if $mod->{id} == $moderation->{id};
+        my $mms = $self->select(id => $mod->{id});
+        push $unchanged_scores, $self->_get_fixed_scores($mod, $mms);
+    }
+    my $base_scores = {
+                      points => reduce { $a->{points} + $b->{points} } $unchanged_scores,
+                      karma => reduce { $a->{karma} + $b->{karma} } $unchanged_scores,
+                      };
+
+    # calc changed moderation's old score
+    my $old_score = $self->_get_fixed_scores($moderation, $metamods);
+    my $old_total = {
+                     points => floor($base_scores->{points} + $old_score->{points}),
+                     karma => floor($base_scores->{karma} + $old_score->{karma}),
+                    };
+    my $old_result = $self->_get_votes_result($moderation, $metamods);
+    my $old_moderator_scores = $self->_get_fixed_moderator_scores($moderation, $old_result);
+
+    $self->start_transaction;
+
+    # create metamod
+    my $rs = $self->_create_metamodlog($user, $moderation, $value);
+    if (!$rs) {
+        $self->rollback;
+        return;
+    }
+
+    # calc changed moderation's new score
+    my $new_metamod = {
+                       val => $value,
+                       uid => $user->{uid},
+                       mmid => $moderation->{id},
+                       active => 1,
+                     };
+    push @$metamods, $new_metamod;
+    my $new_score = $self->_get_fixed_scores($moderation, $metamods);
+    my $new_total = {
+                     points => floor($base_scores->{points} + $new_score->{points}),
+                     karma => floor($base_scores->{karma} + $new_score->{karma}),
+                    };
+    my $delta = {
+                 points => $new_total->{points} - $old_total->{points},
+                 karma => $new_total->{karma} - $old_total->{karma},
+                };
+
+    # update comment's point
+    my $comments = $self->new_instance_of("Comments");
+    my $rs = $comments->update(cid => $moderation->{cid},
+                               points => { add => $delta->{points} });
+    if (!$rs) {
+        $self->rollback;
+        return;
+    }
+
+    # update comment author's karma
+    my $users = $self->new_instance_of("Users");
+    $rs = $users->update_info(uid => $moderation->{cuid},
+                              karma => { add => $delta->{karma} });
+    if (!$rs) {
+        $self->rollback;
+        return;
+    }
+
+    # update moderator's info
+    my $new_result = $self->_get_votes_result($moderation, $metamods);
+    my $new_moderator_scores = $self->_get_fixed_moderator_scores($moderation, $old_result);
+    my $u_delta = {};
+    for my $k (qw(karma tokens up_fair down_fair up_unfair down_unfair)) {
+        $u_delta->{$k} = $old_result->{$k} - $new_result->{$k};
+    }
+    $users->update_info(uid => $moderation->{uid},
+                        tokens => { add => $u_delta->{tokens} },
+                        up_fair => { add => $u_delta->{up_fair} },
+                        down_fair => { add => $u_delta->{down_fair} },
+                        up_unfair => { add => $u_delta->{up_unfair} },
+                        down_unfair => { add => $u_delta->{down_unfair} });
+
+    # update metamoderator's info
+    my $new_params = {};
+    $new_params->{m2_fair} = 1 if $value > 0;
+    $new_params->{m2_unfair} = 1 if $value < 0;
+    if (($value > 0) && ($moderation->{val} > 0)) {
+        $new_params->{m2voted_up_fair} = 1;
+    }
+    if (($value > 0) && ($moderation->{val} < 0)) {
+        $new_params->{m2voted_down_fair} = 1;
+    }
+    if (($value < 0) && ($moderation->{val} > 0)) {
+        $new_params->{m2voted_up_unfair} = 1;
+    }
+    if (($value < 0) && ($moderation->{val} < 0)) {
+        $new_params->{m2voted_down_unfair} = 1;
+    }
+    $users->update_info(uid => $user->{uid}, %$new_params);
+
+    $self->commit;
+    return 1;
+}
+
+sub _get_fixed_moderator_scores {
+    my ($self, $mod, $result) = @_;
+    my $rs = {
+              karma => 0,
+              tokens => 0,
+              up_fair => 0,
+              down_fair => 0,
+              up_unfair => 0,
+              down_unfair => 0,
+             };
+
+    return $rs if $result->{result} == 0;
+
+    my $max_ratio = 0;
+    my $p;
+    my $votes = $result->{votes};
+
+    if ($result->{result} == 1) {
+        $p = $m2_params->{token_fair_params};
+    }
+    elsif ($result->{result} == -1) {
+        $p = $m2_params->{token_unfair_params};
+    }
+
+    if ($p) {
+        if ($votes <= $p->{min_count}) {
+            $max_ratio = $p->{min};
+        }
+        elsif ($votes >= $p->{max_count}) {
+            $max_ratio = $p->{max};
+        }
+        else {
+            $max_ratio = 
+              log(($votes - $p->{min_count}) * $p->{alpha})/log(10) * $p->{beta} + $p->{min};
+        }
+    }
+    $rs->{tokens} = $result->{result} * $max_ratio * $result->{ratio};
+
+    if ($result->{result} == 1) {
+        $rs->{karma} = 0.5;
+        $rs->{up_fair} = 1 if $mod->{val} > 0;
+        $rs->{down_fair} = 1 if $mod->{val} < 0;
+    }
+    elsif ($result->{result} == -1) {
+        $rs->{karma} = -1;
+        $rs->{up_unfair} = 1 if $mod->{val} > 0;
+        $rs->{down_unfair} = 1 if $mod->{val} < 0;
+    }
+    return $rs;
+}
+
+sub _get_fixed_scores {
+    my ($self, $mod, $metamods) = @_;
+    return if !defined $mod;
+    my $rs = {
+              points => 0,
+              karma => 0,
+             };
+    return $rs if !$mod->{active};
+    return $rs if !defined $metamods;
+
+    my $affect = $self->_get_votes_result($mod, $metamods);
+
+    if ($affect->{result} > 0) {
+        # nod
+        $rs->{points} = $mod->{val} * (1.0 + $affect->{enforce});
+        $rs->{karma} = $mod->{val} * (1.0 + $affect->{enforce});
+    }
+    elsif ($affect->{result} < 0) {
+        # nix
+        $rs->{points} = $mod->{val} * (1.0 - $affect->{prevent});
+        $rs->{karma} = $mod->{val} * (1.0 - $affect->{prevent});
+    }
+    else {
+        # no agreement
+        $rs->{points} = $mod->{val};
+        $rs->{karma} = $mod->{val};
+    }
+    return $rs;
+}
+
+sub _get_votes_result {
+    my ($self, $mod, $metamods) = @_;
+    my $count = @$metamods;
+    my $rs = {
+              result => 0,
+              ratio => 0,
+              enforce => 0,
+              prevent => 0,
+              votes => $count,
+             };
+    if ($count < $m2_params->{count_threshold}) {
+        return $rs;
+    }
+
+    my $nods = grep { $_->{val} == 1 && $_->{active} } @$metamods;
+    my $nixes = grep { $_->{val} == -1 && $_->{active} } @$metamods;
+
+    my $fraction = $nods / $count;
+    if ($fraction > $m2_params->{agreement_threshold_ratio}) {
+        $rs->{result} = 1;
+        $rs->{ratio} = $fraction;
+    }
+    else {
+        $fraction = $nixes / $count;
+        if ($fraction > $m2_params->{agreement_threshold_ratio}) {
+            $rs->{result} = -1;
+            $rs->{ratio} = $fraction;
+        }
+    }
+
+    if ($rs->{result} == 0) {
+        # no agreement
+        return $rs;
+    }
+    elsif ($rs->{result} < 0) {
+        # agreement is nix
+        $rs->{prevent} =
+          $self->_calc_ratio($m2_params->{prevent_params}, $count, $fraction);
+    }
+    elsif ($rs->{result} > 0) {
+        # agreement is nod
+        $rs->{enforce} =
+          $self->_calc_ratio($m2_params->{enfoce_params}, $count, $fraction);
+    }
+    return $rs;
+
+}
+
+sub _calc_ratio {
+    my ($self, $p, $votes, $agreement_ratio) = @_;
+    my $max_prevent_ratio;
+    if ($votes <= $p->{min_count}) {
+        $max_prevent_ratio = $p->{min};
+    }
+    elsif ($votes >= $p->{max_count}) {
+        $max_prevent_ratio = $p->{max};
+    }
+    else {
+        $max_prevent_ratio =
+          log(($votes - $p->{min_count}) * $p->{alpha})/log(10) * $p->{beta} + $p->{min};
+    }
+    return $max_prevent_ratio - $p->{reduction} * (1.0 - $agreement_ratio);
+}
+
+sub select {
+    my $self = shift;
+    my $params = {@_};
+    if ($params->{moderation_id}) {
+        $params->{mmid} = $params->{moderation_id};
+    }
+    my $uniques = [qw(id)];
+    my $keys = [qw(uid val mmid)];
+    return $self->base_select("metamodlog", $uniques, $keys, [%$params]);
+}
+
+sub _create_metamodlog {
+    my ($self, $user, $moderation, $value) = @_;
+    my $dbh = $self->connect_db;
+    my $sql = <<"EOSQL";
+INSERT INTO metamodlog
+    (mmid, uid, val, ts, active)
+  VALUES
+    (?,    ?,   ?,   NOW(), 1)
+EOSQL
+
+    my $rs = $dbh->do($sql, undef, $moderation->{id}, $user->{uid}, $value);
+    if (!$rs) {
+        $self->disconnect_db;
+        return;
+    }
+    $self->disconnect_db;
+    return $rs;
+}
+
+
 
 # import from ::Metamod
 sub getM2Needed {
@@ -204,19 +538,6 @@ EOSQL
     return 1;
 }
 
-sub create {
-    my ($self, $params, $user, $extra_params, $opts) = @_;
-    my $users = $self->new_instance_of("Users");
-    my $user_info = $users->select(target => "info", uid => $user->{uid});
-
-    # user validation
-    if (!$user_info->{m2_mods_saved}
-        && !$user->{is_admin}
-        && !$extra_params->{inherited}) {
-        return;
-    }
-}
-
 sub adjustForNewMod {
     my ($self, $user, $comment, $reason, $active, $mod_id) = @_;
     my $i_m2s = $self->getInheritedM2sForMod($user, $comment, $reason, $active, $mod_id);