diff --git a/app/models/concerns/reviewable.rb b/app/models/concerns/reviewable.rb index 31a9580847d997fa6f6111cc62ddc281a3c92752..82d9391d3b9d2b14387de5874986e740cd986946 100644 --- a/app/models/concerns/reviewable.rb +++ b/app/models/concerns/reviewable.rb @@ -5,4 +5,8 @@ module Reviewable has_many :reviews, as: :reviewable, dependent: :destroy end + def review_ratings_average + array_average(reviews.map { |review| review.rating_average }) + end + end diff --git a/app/models/score.rb b/app/models/score.rb new file mode 100644 index 0000000000000000000000000000000000000000..508061b5f69f50f05b38a3c2f3f11d5cf8c14d79 --- /dev/null +++ b/app/models/score.rb @@ -0,0 +1,16 @@ +class Score < ActiveRecord::Base + has_many :score_user_categories, dependent: :destroy + has_many :user_categories, through: :score_user_categories + + validates_presence_of :name, :code, :weight + validates_uniqueness_of :code + + def max_value + score_user_categories.maximum(:value) + end + + def category_value(category) + score = score_user_categories.where(score: self, user_category: category).first + (score.nil?) ? nil : score.value + end +end diff --git a/app/models/score_user_category.rb b/app/models/score_user_category.rb new file mode 100644 index 0000000000000000000000000000000000000000..1ff802069cdccaf1e0c52dec1628f9a01d308f21 --- /dev/null +++ b/app/models/score_user_category.rb @@ -0,0 +1,34 @@ +class ScoreUserCategory < ActiveRecord::Base + belongs_to :score + belongs_to :user_category + + validates_presence_of :score, :user_category, :value + validates_uniqueness_of :user_category, scope: :score + + after_commit :calculate_reference + + private + + def calculate_reference + return 0 if Score.count < 9 || !reputation_hash.values.all? { |x| !x.nil? } + + reference = ScoreCalculatorService.new(self).reputation(reputation_hash) + + user_category.update(reference: reference) if reference.is_a? Float + end + + def reputation_hash + { + 'reputation_submitted': Score.where(code: 'reputation_submitted').first.category_value(user_category), + 'reputation_reviews_average_score': Score.where(code: 'reputation_reviews_average_score').first.category_value(user_category), + 'reputation_added_in_best_collections': Score.where(code: 'reputation_added_in_best_collections').first.category_value(user_category), + 'reputation_best_score': Score.where(code: 'reputation_best_score').first.category_value(user_category), + 'reputation_average_score': Score.where(code: 'reputation_average_score').first.category_value(user_category), + 'reputation_reviews_rate': Score.where(code: 'reputation_reviews_rate').first.category_value(user_category), + # 'reputation_confirmed_complaints': Score.where(code: 'reputation_confirmed_complaints').first.category_value(user_category), + 'reputation_followers': Score.where(code: 'reputation_followers').first.category_value(user_category), + 'reputation_submitted_recently': Score.where(code: 'reputation_submitted_recently').first.category_value(user_category), + 'reputation_collection_score': Score.where(code: 'reputation_collection_score').first.category_value(user_category) + } + end +end diff --git a/app/models/topic_relationship.rb b/app/models/topic_relationship.rb index 5dd3f84bb5ab5e127c95f4725304d88af5059f71..c5007d12988ade58cc2cd036f90fcd1aabed0b71 100644 --- a/app/models/topic_relationship.rb +++ b/app/models/topic_relationship.rb @@ -3,4 +3,5 @@ class TopicRelationship < ActiveRecord::Base belongs_to :child, class_name: 'Topic' validates_presence_of :parent, :child + validates_uniqueness_of :child, scope: :parent end diff --git a/app/models/user.rb b/app/models/user.rb index 301f842067bf3edb30a0a34fdd4abc77acf1cc96..04022015af830932764eb2112c5ea69479568500 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -44,6 +44,20 @@ class User < ActiveRecord::Base false end + def review_approval_average + array = reviews.map(&:rates_percentage) + array.inject(0.0) { |sum, el| sum + el } / array.size + end + + def learning_objects_in_best_collections + average = Collection.average(:score) + collections.where(:score > average).count + end + + def learning_objects_submitted_recently + learning_objects.where(:created_at > (Time.now - 2.months)).count + end + private def default_role diff --git a/app/models/user_category.rb b/app/models/user_category.rb new file mode 100644 index 0000000000000000000000000000000000000000..dec0e310c6de38e1cf8f8c5bad8bad87895fe2a9 --- /dev/null +++ b/app/models/user_category.rb @@ -0,0 +1,7 @@ +class UserCategory < ActiveRecord::Base + has_many :score_user_categories, dependent: :destroy + has_many :scores, through: :score_user_categories + + validates_presence_of :name + validates_uniqueness_of :name +end diff --git a/app/services/score_calculator_service.rb b/app/services/score_calculator_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..e238c19363f5e733f10419c9bd72af8acc4ae88e --- /dev/null +++ b/app/services/score_calculator_service.rb @@ -0,0 +1,49 @@ +class ScoreCalculatorService + + def initialize(object) + @object = object + @class = @object.class.name + end + + def calculate + case @class + when 'User' then reputation + end + end + + def reputation(values = nil) + values = reputation_hash if values.nil? + + sum = 0 + + Score.all.find_each do |score| + next if score.max_value.to_f == 0 + + sum += (values[score.code.to_sym].to_f / score.max_value.to_f) * score.weight.to_f + end + + sum.to_f / sum_weights.to_f + end + + private + + def reputation_hash + { + 'reputation_submitted': @object.learning_objects.count, + 'reputation_reviews_average_score': @object.learning_objects.review_ratings_average, + 'reputation_added_in_best_collections': @object.learning_objects_in_best_collections, + 'reputation_best_score': @object.learning_objects.maximum(:score), + 'reputation_average_score': @object.learning_objects.average(:score), + 'reputation_reviews_rate': @object.review_approval_average, + # 'reputation_confirmed_complaints': , + 'reputation_followers': @object.followers.count, + 'reputation_submitted_recently': @object.learning_objects_submitted_recently, + 'reputation_collection_score': @object.where(privacy: 'public').average(:score) + } + end + + def sum_weights + Score.pluck(:weight).inject(0.0) { |a, e| a + e } + end + +end diff --git a/app/workers/score_calculator_worker.rb b/app/workers/score_calculator_worker.rb index 5654058c0f0821fbfb03dee191c446945a2d97b5..5a0e9b820fd7f907d2337a7af81a4f780166b84c 100644 --- a/app/workers/score_calculator_worker.rb +++ b/app/workers/score_calculator_worker.rb @@ -2,59 +2,13 @@ class ScoreCalculatorWorker include Sidekiq::Worker sidekiq_options queue: :score - def perform(rid) - object = LearningObject.find(rid) + def perform(id, object_class) + return false unless %w(User LearningObject Collection).include? object_class - unless object.blank? - # Weights to score. Sum must be 1000 - weights = { - "thumbnail": 250, - "description": 150, - "likes": 250, - "views": 150, - "downloads":200 - } + object = object_class.constantize.find(id) - score = 0 + score = ScoreCalculatorService.new(object).calculate - # 250 points if it has thumbnail - score += weights[:thumbnail] unless object.thumbnail.blank? - - # 150 points if it has description - score += weights[:description] unless object.description.blank? - - # range to 250 points, for normalized likes ( maxLikes/actualLike => [0..1] ) - score += divide_by_max( - LearningObject.count('Likes', object), - LearningObject.max('Likes'), - weights[:likes] - ) - - # range to 200 points, for normalized views ( maxLikes/actualLike => [0..1] ) - score += divide_by_max( - LearningObject.count('Views', object), - LearningObject.max('Views'), - weights[:views] - ) - - # downloads - score += divide_by_max( - LearningObject.count('Downloads', object), - LearningObject.max('Downloads'), - weights[:downloads] - ) - - LearningObject.update_property(object, 'score', score) - end - end - - private - - def divide_by_max(i, max, weight) - if (i > 0 && max > 0) && (i <= max) - (i / max) * weight - else - 0 - end + object.update(score: score) end end diff --git a/db/migrate/20160222144157_create_scores.rb b/db/migrate/20160222144157_create_scores.rb new file mode 100644 index 0000000000000000000000000000000000000000..736b07385090b4ba4b9e17dcf253ffa30f4b9f1e --- /dev/null +++ b/db/migrate/20160222144157_create_scores.rb @@ -0,0 +1,12 @@ +class CreateScores < ActiveRecord::Migration + def change + create_table :scores do |t| + t.string :name + t.string :code + t.float :weight + t.boolean :active, default: true + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20160222144216_create_user_categories.rb b/db/migrate/20160222144216_create_user_categories.rb new file mode 100644 index 0000000000000000000000000000000000000000..fa97c78f2e6ecf4b0313d6b1725323426995236e --- /dev/null +++ b/db/migrate/20160222144216_create_user_categories.rb @@ -0,0 +1,10 @@ +class CreateUserCategories < ActiveRecord::Migration + def change + create_table :user_categories do |t| + t.string :name + t.float :reference + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20160222144259_create_score_user_categories.rb b/db/migrate/20160222144259_create_score_user_categories.rb new file mode 100644 index 0000000000000000000000000000000000000000..b41e57f2e627d5d53ded3c2f86b4cab0a4d64e6b --- /dev/null +++ b/db/migrate/20160222144259_create_score_user_categories.rb @@ -0,0 +1,13 @@ +class CreateScoreUserCategories < ActiveRecord::Migration + def change + create_table :score_user_categories do |t| + t.references :score, index: true + t.references :user_category, index: true + t.float :value + + t.timestamps null: false + end + add_foreign_key :score_user_categories, :scores + add_foreign_key :score_user_categories, :user_categories + end +end diff --git a/db/migrate/20160225101700_change_users_add_score.rb b/db/migrate/20160225101700_change_users_add_score.rb new file mode 100644 index 0000000000000000000000000000000000000000..ac9d75bb8b448bb7600890339b737e73138db8fe --- /dev/null +++ b/db/migrate/20160225101700_change_users_add_score.rb @@ -0,0 +1,6 @@ +class ChangeUsersAddScore < ActiveRecord::Migration + def change + add_reference :users, :user_category, index: true + add_column :users, :score, :float, default: 0 + end +end diff --git a/db/seeds.rb b/db/seeds.rb index 6de731e6b8f0c6cc3e49d9545d035885e470f8c5..ef2acb24fd81c05dfad076712fb71a6c9b7136cb 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -18,8 +18,9 @@ User.create( ) # data for portalmec -require_relative "seeds/complaints" -require_relative "seeds/institutions" -require_relative "seeds/languages" -require_relative "seeds/ratings" -require_relative "seeds/object_types" +require_relative 'seeds/complaints' +require_relative 'seeds/institutions' +require_relative 'seeds/languages' +require_relative 'seeds/object_types' +require_relative 'seeds/ratings' +require_relative 'seeds/scores' diff --git a/db/seeds/scores.rb b/db/seeds/scores.rb new file mode 100644 index 0000000000000000000000000000000000000000..7d97c0ec00582e1dbbed64a19714be32156ea0d9 --- /dev/null +++ b/db/seeds/scores.rb @@ -0,0 +1,41 @@ +# objects + +# reputation categories +categories = [ + { name: 'Iniciante' }, + { name: 'Novato' }, + { name: 'Amador' }, + { name: 'Profissional' }, + { name: 'Expert' } +].reverse + +# reputation +reputations = [ + { name: 'Quantidade de envio de objetos', code: 'reputation_submitted', weight: 4, active: true }, + { name: 'Avaliação média dos seus objetos', code: 'reputation_reviews_average_score', weight: 9, active: true }, + { name: 'Adições a coleções bem avaliadas', code: 'reputation_added_in_best_collections', weight: 7, active: true }, + { name: 'Melhor score', code: 'reputation_best_score', weight: 3, active: true }, + { name: 'Score médio', code: 'reputation_average_score', weight: 7, active: true }, + { name: 'Aprovação das avaliações em objetos de terceiros (% de sim)', code: 'reputation_reviews_rate', weight: 10, active: true }, + # { name: 'Denúncias confirmadas', code: 'reputation_confirmed_complaints', weight: 0, active: true }, + { name: 'Quantidade de seguidores', code: 'reputation_followers', weight: 6, active: true }, + { name: 'Submissões recentes', code: 'reputation_submitted_recently', weight: 4, active: true }, + { name: 'Score das coleções públicas', code: 'reputation_collection_score', weight: 3, active: true } +] + +values = [ + [ 0, 0, 0, 0, 0, 0, 0, 0, 0], # Iniciante + [ 25, 3.0, 10, 500, 250, 70, 50, 5, 400], # Novato + [ 50, 3.8, 20, 550, 350, 85, 250, 15, 600], # Amador + [ 100, 4.2, 40, 600, 450, 90, 1000, 25, 750], # Profissional + [ 250, 4.9, 80, 680, 550, 95, 5000, 35, 800] # Expert +].reverse + +# iterate to make associations +categories.each_with_index do |c, i| + category = UserCategory.where(c).first_or_create + reputations.each_with_index do |r, j| + reputation = Score.where(r).first_or_create + ScoreUserCategory.create(user_category: category, score: reputation, value: values[i][j]) + end +end diff --git a/test/fixtures/bookmarks.yml b/test/fixtures/bookmarks.yml index d9098d9934714fbe4ea20f4003a7c8beacfb73c6..eb518fa5279ce269f068a328b0516cb134e29af9 100644 --- a/test/fixtures/bookmarks.yml +++ b/test/fixtures/bookmarks.yml @@ -1,7 +1,13 @@ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html -one: - user_id: +<% 1.upto(5000) do |i| %> +bookmark_top_<%= i %>: + user: user_<%= i %> + bookmarkable: lo_top (LearningObject) +<% end %> -two: - user_id: +<% 1.upto(3000) do |i| %> +bookmark_joao_<%= i %>: + user: joao + bookmarkable: lo_test_<%= i %> (LearningObject) +<% end %> diff --git a/test/fixtures/collections.yml b/test/fixtures/collections.yml index be5d2e5138faca1e2c1b2c1a69b6fd2db1c3422a..8accfb840e1d7249d47eb7e203d52128725019e9 100644 --- a/test/fixtures/collections.yml +++ b/test/fixtures/collections.yml @@ -1,11 +1,12 @@ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html -one: - name: MyString - description: MyText - privacy: MyString - -two: - name: MyString - description: MyText - privacy: MyString +<% 1.upto(20000) do |i| %> +collection_test_<%= i %>: + name: 'lo_test_<%= i %>' + description: 'hue hue hue test <%= i %>' + created_at: 2016-01-28 09:51:07 + views: <% 1.upto(9999) do |i| %>view_<%= i %>,<% end %>view_10000 + likes: <% 1.upto(2799) do |i| %>like_<%= i %>,<% end %>like_2800 + downloads: <% 1.upto(5999) do |i| %>download_<%= i %>,<% end %>download_6000 + shares: <% 1.upto(2999) do |i| %>share_<%= i %>,<% end %>share_3000 +<% end %> diff --git a/test/fixtures/downloads.yml b/test/fixtures/downloads.yml index 1afd2aa2c6cc5e33cf90cf2d48fe9b3f20b3a653..4a4858e7054582501099cfb8c4747f458005412e 100644 --- a/test/fixtures/downloads.yml +++ b/test/fixtures/downloads.yml @@ -1,7 +1,6 @@ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html -one: - date: 2016-01-28 09:54:28 - -two: - date: 2016-01-28 09:54:28 +<% 1.upto(10000) do |i| %> +download_<%= i %>: + date: 2016-01-28 09:51:07 +<% end %> diff --git a/test/fixtures/follows.yml b/test/fixtures/follows.yml index 937a0c002e426861e33bb25a2a8ce2b20b3efaa3..a11b3da63d835635e3f39e50d823db8f7ba240c9 100644 --- a/test/fixtures/follows.yml +++ b/test/fixtures/follows.yml @@ -1,11 +1,7 @@ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# -one: {} -# column: value -# -two: {} -# column: value +<% 1.upto(20000) do |i| %> +follow_<%= i %>: + user: + followable: +<% end %> diff --git a/test/fixtures/learning_objects.yml b/test/fixtures/learning_objects.yml index 7c75fb6710d6fc594b31b98a3a5d720bf587e0ff..7a482571fa9d54bd09faaaa99c303bfada0ba219 100644 --- a/test/fixtures/learning_objects.yml +++ b/test/fixtures/learning_objects.yml @@ -1,25 +1,37 @@ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html -one: - id_dspace: 1 - name: MyString - author: MyString - description: MyText - published_at: 2016-01-25 10:09:28 - type: - score: 1 - school_level: 1 - language: MyString - keywords: MyText +lo1: + name: 'lo1' + description: 'hue hue hue 1' + reviews: + thumbnail: '' + score: 0.0 + created_at: 2016-01-28 09:51:07 + views: <% 1.upto(9999) do |i| %>view_<%= i %>,<% end %>view_10000 + likes: <% 1.upto(2799) do |i| %>like_<%= i %>,<% end %>like_2800 + downloads: <% 1.upto(5999) do |i| %>download_<%= i %>,<% end %>download_6000 + shares: <% 1.upto(2999) do |i| %>share_<%= i %>,<% end %>share_3000 -two: - id_dspace: 1 - name: MyString - author: MyString - description: MyText - published_at: 2016-01-25 10:09:28 - type: - score: 1 - school_level: 1 - language: MyString - keywords: MyText +lo_top: + name: 'lo_top' + description: 'hue hue hue top' + reviews: + thumbnail: '' + score: 0.0 + created_at: 2016-01-28 09:51:07 + views: <% 1.upto(19999) do |i| %>view_<%= i %>,<% end %>view_20000 + likes: <% 1.upto(3499) do |i| %>like_<%= i %>,<% end %>like_3500 + downloads: <% 1.upto(7999) do |i| %>download_<%= i %>,<% end %>download_8000 + shares: <% 1.upto(3999) do |i| %>share_<%= i %>,<% end %>share_4000 + +<% 1.upto(10000) do |i| %> +lo_test_<%= i %>: + name: 'lo_test_<%= i %>' + description: 'hue hue hue test <%= i %>' + thumbnail: '' + created_at: 2016-01-28 09:51:07 + views: <% 1.upto(9999) do |i| %>view_<%= i %>,<% end %>view_10000 + likes: <% 1.upto(2799) do |i| %>like_<%= i %>,<% end %>like_2800 + downloads: <% 1.upto(5999) do |i| %>download_<%= i %>,<% end %>download_6000 + shares: <% 1.upto(2999) do |i| %>share_<%= i %>,<% end %>share_3000 +<% end %> diff --git a/test/fixtures/likes.yml b/test/fixtures/likes.yml index 9dc57b68dd5ad1d883dda1bc093a8900ef1e745e..dba27447649804f1c471041d4940b86ba975b616 100644 --- a/test/fixtures/likes.yml +++ b/test/fixtures/likes.yml @@ -1,7 +1,6 @@ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html -one: - date: 2016-01-28 09:55:55 - -two: - date: 2016-01-28 09:55:55 +<% 1.upto(10000) do |i| %> +like_<%= i %>: + date: 2016-01-28 09:51:07 +<% end %> diff --git a/test/fixtures/score_user_categories.yml b/test/fixtures/score_user_categories.yml new file mode 100644 index 0000000000000000000000000000000000000000..8b48ec7eac837b9208529a5480b336b045e3a0a0 --- /dev/null +++ b/test/fixtures/score_user_categories.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + score_id: + user_category_id: + value: 1 + +two: + score_id: + user_category_id: + value: 1 diff --git a/test/fixtures/scores.yml b/test/fixtures/scores.yml new file mode 100644 index 0000000000000000000000000000000000000000..bd103ffc7b605d6340700a90e69a8e3750ad9d49 --- /dev/null +++ b/test/fixtures/scores.yml @@ -0,0 +1,13 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + name: MyString + code: MyString + weight: 1 + active: false + +two: + name: MyString + code: MyString + weight: 1 + active: false diff --git a/test/fixtures/shares.yml b/test/fixtures/shares.yml index 18c323239a0eec3362ec5b1f64da298702dc123a..44c28365e12f4099444fc2f7155d4becc19efb98 100644 --- a/test/fixtures/shares.yml +++ b/test/fixtures/shares.yml @@ -1,7 +1,6 @@ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html -one: - date: 2016-01-28 09:57:21 - -two: - date: 2016-01-28 09:57:21 +<% 1.upto(10000) do |i| %> +share_<%= i %>: + date: 2016-01-28 09:51:07 +<% end %> diff --git a/test/fixtures/user_categories.yml b/test/fixtures/user_categories.yml new file mode 100644 index 0000000000000000000000000000000000000000..56066c68af42f307f5780e9ac146e3651cea3979 --- /dev/null +++ b/test/fixtures/user_categories.yml @@ -0,0 +1,7 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + name: MyString + +two: + name: MyString diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index e7c2ceefe67d37c925d3243bfb4110b900ec03a8..8d6fe0462c36c286c3493fd9879a0c8890642e2e 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -1,7 +1,11 @@ -one: - email: 'mgg12@inf.ufpr.br' - provider: 'email' +joao: + name: 'João' + email: 'joao@teste.br' + score: '0.0' + learning_objects: lo1, lo2, lo3 -two: - email: 'test@c3sl.ufpr.br' - provider: 'email' \ No newline at end of file +<% 1.upto(10000) do |i| %> +user_<%= i %>: + name: 'user test <%= i %>' + email: 'user_test_<%= i %>@test.com' +<% end %> diff --git a/test/fixtures/views.yml b/test/fixtures/views.yml index 2450a5d83c100365f3b24912a5ba086970603d0a..fd91cd03e4eb035d1a3f5d1df6c54f8cf4496bf1 100644 --- a/test/fixtures/views.yml +++ b/test/fixtures/views.yml @@ -1,7 +1,6 @@ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html -one: - date: 2016-01-28 09:51:07 - -two: +<% 1.upto(20000) do |i| %> +view_<%= i %>: date: 2016-01-28 09:51:07 +<% end %> diff --git a/test/models/score_test.rb b/test/models/score_test.rb new file mode 100644 index 0000000000000000000000000000000000000000..d38cb0a3c55492dad6707691aa49fa19fb9b7987 --- /dev/null +++ b/test/models/score_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class ScoreTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/score_user_category_test.rb b/test/models/score_user_category_test.rb new file mode 100644 index 0000000000000000000000000000000000000000..bd5bf9f3167904855c052af49b181f97a25019aa --- /dev/null +++ b/test/models/score_user_category_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class ScoreUserCategoryTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/user_category_test.rb b/test/models/user_category_test.rb new file mode 100644 index 0000000000000000000000000000000000000000..4cdbac46363829d8f97cff9140dd589eb4ba466b --- /dev/null +++ b/test/models/user_category_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class UserCategoryTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/services/score_calculator_service_test.rb b/test/services/score_calculator_service_test.rb new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391