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 @@ | @@ -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 |