Commit 046756e4dfafa863b7c8fc305227bcaf42acb452
1 parent
70917f62
Exists in
master
and in
1 other branch
Create script for searching and fixing people points
Showing
1 changed file
with
156 additions
and
0 deletions
Show diff stats
| ... | ... | @@ -0,0 +1,156 @@ |
| 1 | +#!/usr/bin/env ruby | |
| 2 | +# encoding: UTF-8 | |
| 3 | + | |
| 4 | +# | |
| 5 | +# This script was created for ensuring all the actions observed | |
| 6 | +# by merit for pontuation was judged and pontuated accordingly | |
| 7 | +# It checks the merit_actions registers for each action(model | |
| 8 | +# create or destroy) and recreates it | |
| 9 | +# | |
| 10 | + | |
| 11 | +require 'csv' | |
| 12 | + | |
| 13 | +class ProcessObserver | |
| 14 | + def update(changed_data) | |
| 15 | + merit = changed_data[:merit_object] | |
| 16 | + if merit.kind_of?(Merit::Score::Point) | |
| 17 | + action = Merit::Action.find(changed_data[:merit_action_id]) | |
| 18 | + new_date = YAML.load(action.target_data).created_at | |
| 19 | + action.update_attribute(:created_at, new_date) | |
| 20 | + merit.update_attribute(:created_at, new_date) | |
| 21 | + end | |
| 22 | + end | |
| 23 | +end | |
| 24 | + | |
| 25 | +def create_action(obj, index, count) | |
| 26 | + target_model = obj.class.base_class.name.downcase | |
| 27 | + action = Merit::Action.find_by_target_id_and_target_model_and_action_method(obj.id, target_model, 'create') | |
| 28 | + if action.nil? | |
| 29 | + puts "#{index}/#{count} Create merit action for #{target_model} #{obj.id}" | |
| 30 | + begin | |
| 31 | + obj.new_merit_action(:create) | |
| 32 | + rescue Exception => e | |
| 33 | + puts "Could not be create: #{e.message}" | |
| 34 | + end | |
| 35 | + end | |
| 36 | +end | |
| 37 | + | |
| 38 | +def recreate_actions person, objects, category | |
| 39 | + puts "Recreating actions for #{person.identifier} on model #{objects.first.class.base_class.name}" | |
| 40 | + actions = Merit::Action.where(target_id: objects, target_model: objects.first.class.base_class.name.downcase, action_method: 'create') | |
| 41 | + Merit::Score::Point.where(action_id: actions).destroy_all | |
| 42 | + actions.destroy_all | |
| 43 | + # erase remaining points if any (can be wrong on destroy cases ?) | |
| 44 | + person.score_points.where(score_id: Merit::Score.where(category: category)).destroy_all | |
| 45 | + count = objects.count | |
| 46 | + objects.each_with_index{ |obj, index| create_action(obj, index, count) } | |
| 47 | +end | |
| 48 | + | |
| 49 | +def calc_points categorization, objects | |
| 50 | + rule = Merit::PointRules::AVAILABLE_RULES[categorization.point_type.name.to_sym] | |
| 51 | + return 0 if rule.nil? | |
| 52 | + | |
| 53 | + sum = objects.map{|o| rule[:value].respond_to?(:call) ? rule[:value].call(o) : rule[:value] }.sum | |
| 54 | + return sum * categorization.weight | |
| 55 | +end | |
| 56 | + | |
| 57 | +# avoid updating level on every action for increasing performance | |
| 58 | +Merit.observers.delete('RankObserver') | |
| 59 | + | |
| 60 | +Merit.observers << 'ProcessObserver' | |
| 61 | + | |
| 62 | +class Article < ActiveRecord::Base | |
| 63 | + def self.text_article_types | |
| 64 | + ['ProposalsDiscussionPlugin::Proposal'] | |
| 65 | + end | |
| 66 | +end | |
| 67 | + | |
| 68 | +puts "Creaning up points from actions which don't exist" | |
| 69 | +Merit::Score::Point.includes(:action).find_each(batch_size: 100) do |point| | |
| 70 | + point.destroy if point.action.nil? | |
| 71 | +end | |
| 72 | + | |
| 73 | +Environment.all.each do |environment| | |
| 74 | + puts "Process environment #{environment.name}" | |
| 75 | + | |
| 76 | + Merit::AppPointRules.clear | |
| 77 | + Merit::AppBadgeRules.clear | |
| 78 | + Merit::AppPointRules.merge!(Merit::PointRules.new(environment).defined_rules) | |
| 79 | + Merit::AppBadgeRules.merge!(Merit::BadgeRules.new(environment).defined_rules) | |
| 80 | + | |
| 81 | + group_control = YAML.load(File.read(File.join(Rails.root,'tmp','control_group.yml'))) if File.exist?(File.join(Rails.root,'tmp','control_group.yml')) | |
| 82 | + conditions = group_control.nil? ? {} : {:identifier => group_control.map{|k,v| v['profiles']}.flatten} | |
| 83 | + people_count = environment.people.where(conditions).count | |
| 84 | + person_index = 0 | |
| 85 | + remaining_wrong_points = [] | |
| 86 | + puts "Analising environment people" | |
| 87 | + environment.people.find_each(:conditions => conditions) do |person| | |
| 88 | + person_index += 1 | |
| 89 | + profile_ids = GamificationPlugin::PointsCategorization.uniq.pluck(:profile_id) | |
| 90 | + profile_ids.keep_if { |item| group_control.keys.include?(item) } unless group_control.nil? | |
| 91 | + profile_ids.each do |profile_id| | |
| 92 | + profile = Profile.where(id: profile_id).first | |
| 93 | + if profile.nil? | |
| 94 | + profile_name = 'generic' | |
| 95 | + # person actions | |
| 96 | + person_articles = Article.where(author_id: person.id) | |
| 97 | + comments = Comment.where(author_id: person.id) | |
| 98 | + votes = Vote.for_voter(person) | |
| 99 | + follows = ArticleFollower.where(person_id: person.id) | |
| 100 | + else | |
| 101 | + profile_name = profile.identifier | |
| 102 | + #person actions | |
| 103 | + person_articles = Article.where(author_id: person.id, profile_id: profile) | |
| 104 | + comments = Comment.where(author_id: person.id, source_id: profile.articles) | |
| 105 | + general_votes = Vote.for_voter(person) | |
| 106 | + votes = general_votes.where("(voteable_type = 'Article' and voteable_id in (?)) or (voteable_type = 'Comment' and voteable_id in (?))",profile.articles, Comment.where(source_type: "Article", source_id: profile.articles)) | |
| 107 | + follows = ArticleFollower.where(person_id: person.id, article_id: profile.articles) | |
| 108 | + end | |
| 109 | + # received actions | |
| 110 | + comments_received = Comment.where(:source_id => person_articles) | |
| 111 | + votes_received = Vote.where("(voteable_type = 'Article' and voteable_id in (?)) or (voteable_type = 'Comment' and voteable_id in (?))",person_articles, person.comments) | |
| 112 | + follows_received = ArticleFollower.where(:article_id => person_articles) | |
| 113 | + | |
| 114 | + puts "#{person_index}/#{people_count} - Analising points for #{person.identifier} on #{profile_name}" | |
| 115 | + puts "Proposed #{person_articles.count} times, Commented #{comments.count} times, Voted #{votes.count} times, Followed #{follows.count} times" | |
| 116 | + puts "Received #{votes_received.count} votes, #{comments_received.count} comments, #{follows_received.count} follows\n" | |
| 117 | + | |
| 118 | + scope_by_type = { | |
| 119 | + article_author: person_articles, comment_author: comments, vote_voter: votes, follower: follows, | |
| 120 | + comment_article_author: comments_received, vote_voteable_author: votes_received, followed_article_author: follows_received | |
| 121 | + } | |
| 122 | + | |
| 123 | + puts "Points:" | |
| 124 | + scope_by_type.each do |type, scope| | |
| 125 | + c = GamificationPlugin::PointsCategorization.for_type(type).where(profile_id: profile_id).joins(:point_type).first | |
| 126 | + points = calc_points c, scope | |
| 127 | + puts "On #{c.point_type.name} it should have #{points} and have #{person.points(category: c.id.to_s)} " | |
| 128 | + if points != person.points(category: c.id.to_s) | |
| 129 | + recreate_actions person, scope, c.id.to_s | |
| 130 | + points = calc_points c, scope | |
| 131 | + puts "after recreating points the person has: #{person.reload.points(category: c.id.to_s)} and should have #{points}" | |
| 132 | + remaining_wrong_points << [person.identifier, person.name, scope.first.class.base_class.name, profile_name, c.id, c.point_type.name, scope.count*c.weight, person.points(category: c.id.to_s)] if points != person.points(category: c.id.to_s) | |
| 133 | + end | |
| 134 | + end | |
| 135 | + puts | |
| 136 | + end | |
| 137 | + puts "Updating #{person.identifier} level\n" | |
| 138 | + person.update_attribute(:level, person.gamification_plugin_calculate_level) | |
| 139 | + end | |
| 140 | + | |
| 141 | + # write to the spreadsheet the person points that couldn't regulate | |
| 142 | + unless remaining_wrong_points.blank? | |
| 143 | + CSV.open( "gamification_points_out_expectation.csv", 'w' ) do |csv| | |
| 144 | + csv << ['identifier', 'name', 'action', 'profile', 'category id', 'category type', 'should have', 'have'] | |
| 145 | + remaining_wrong_points.each do |line| | |
| 146 | + csv << line | |
| 147 | + end | |
| 148 | + end | |
| 149 | + end | |
| 150 | + | |
| 151 | + if remaining_wrong_points.count | |
| 152 | + puts "Finished. There was #{remaining_wrong_points.count} people/pontuation types with errors after check and fix. Please check the created spreadsheet." | |
| 153 | + else | |
| 154 | + puts "Finished. There was no errors after checking. \o/ Pontuation seems to be ok!" | |
| 155 | + end | |
| 156 | +end | ... | ... |