diff --git a/.gitignore b/.gitignore
index 44fa97956124383c1edf71da20dd0a109873f14b..5767d2bf65adf0dc83b3068f876ec7a1f4384a70 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,7 @@ Gemfile.lock
 /shared/*
 dump.rdb
 /public/system/*
+/db/seeds/assets/*
 
 # ignore server scripts
 puma.sh
@@ -37,4 +38,4 @@ autocomplete-server.service
 
 # ignore configs
 /config/database.yml
-/config/sidekiq.yml
\ No newline at end of file
+/config/sidekiq.yml
diff --git a/Gemfile b/Gemfile
index c4f85f58e9e218ae0de4f0eecc77cd21546775bc..bf2549e36db20c991a787caf7df06bf03a6d9356 100644
--- a/Gemfile
+++ b/Gemfile
@@ -145,7 +145,11 @@ gem 'activerecord-import'
 # social connect
 gem 'omniauth-facebook'
 gem 'omniauth-twitter'
-gem 'omniauth-google-oauth2', '~>0.8.2'
+gem 'omniauth-google-oauth2', '0.8.2'
+
+gem 'faraday'
+gem 'net-http-persistent'
+
 
 # get mime type
 gem 'mimemagic'
@@ -172,3 +176,7 @@ gem 'paranoia'
 gem 'paper_trail'
 
 gem 'acts_as_list'
+
+gem 'faraday-net_http_persistent', '~> 2.0'
+
+gem 'elasticsearch'
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 7d8ea4cded332be1ef2e742db89cef19020aeae2..897f5611fb3d85a137bbe3f85cae25aaa4b300a4 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -28,8 +28,10 @@ class ApplicationController < ActionController::API
 
   rescue_from Exception do |exception|
     logger.fatal "The request raised an exception:"
+    http_request_headers = request.headers.select{|header_name, header_value| header_name.match("^HTTP.*")}
+    logger.fatal "#{request.method.inspect} request to #{request.url.inspect} from #{request.remote_ip.inspect} with headers #{http_request_headers.inspect} and params #{params.inspect} gave the following exception:"
     logger.fatal exception
-    logger.fatal exception.backtrace.first(10).join("\n")
+    logger.fatal exception.backtrace.first(15).join("\n")
     unless response_body
       render status: :internal_server_error
     end
diff --git a/app/controllers/concerns/items_filter.rb b/app/controllers/concerns/items_filter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b0cb5d1217c251846f1c84c5c9f09427cfed106f
--- /dev/null
+++ b/app/controllers/concerns/items_filter.rb
@@ -0,0 +1,48 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+module ItemsFilter
+  extend ActiveSupport::Concern
+
+  def filter_items(items)
+    # filter by type
+    if !params[:item_type].blank?
+      items = items.where(item_type: params[:item_type])
+    end
+
+    # filter by price
+    if params[:op] == "lt" # less than
+      items = items.where("price < ?", params[:price])
+    elsif params[:op] == "gt" # greater than
+      items = items.where("price > ?", params[:price])
+    elsif params[:op] == "eq" # equals
+      items = items.where(price: params[:price])
+    end
+
+    # filter by the way it's unlocked
+    if params[:unlock_rule] == "achievement"
+      items = items.where.not(achievement_id: nil)
+    elsif params[:unlock_rule] == "purchase"
+      items = items.where(achievement_id: nil)
+    end
+
+    items
+  end
+
+end
diff --git a/app/controllers/v1/achievements_controller.rb b/app/controllers/v1/achievements_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dcb47a03ad7f96c1c6a4f0e94472208a6de80f51
--- /dev/null
+++ b/app/controllers/v1/achievements_controller.rb
@@ -0,0 +1,91 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class V1::AchievementsController < ApplicationController
+  include ::Paginator
+
+  before_action :set_achievement, only: [:show, :update, :destroy]
+  before_action :authenticate_user!, except: [:index, :show]
+  before_action :authorize!, only: [:update, :destroy]
+
+  # GET /v1/achievements
+  def index
+    achievements = paginate Achievement
+    render json: achievements
+  end
+
+  # GET /v1/achievements/1
+  def show
+    render json: @achievement
+  end
+
+  # POST /v1/achievements
+  def create
+    @achievement = Achievement.new(achievement_params)
+    authorize @achievement
+
+    if @achievement.save
+      @achievement.add_requirements(extra_params[:requirements])
+      render json: @achievement, status: :created
+    else
+      render json: @achievement.errors, status: :unprocessable_entity
+    end
+  end
+
+  # PUT/PATCH /v1/achievements/1
+  def update
+    if @achievement.update(achievement_params)
+      @achievement.update_requirements(extra_params[:requirements])
+      render json: @achievement, status: :ok
+    else
+      render json: @achievement.errors, status: :unprocessable_entity
+    end
+  end
+
+  # DELETE /v1/achievements/1
+  def destroy
+    if @achievement.update(state: 2)
+      render status: :ok, json: @achievement
+    else
+      render json: @achievement.errors, status: :unprocessable_entity
+    end
+  end
+
+  private
+
+  def set_achievement
+    @achievement ||= Achievement.find_by_id(params[:id])
+    if @achievement.blank?
+      render status: :not_found
+    end
+  end
+
+  def achievement_params
+    params.require(:achievement).permit(:name, :description, :reward_experience, :reward_points, :state, :repeatable, :resettable)
+  end
+
+  def extra_params
+    return {} if params[:achievement].nil?
+    params[:achievement].permit(requirements: [])
+  end
+
+  def authorize!
+    authorize @achievement
+  end
+end
diff --git a/app/controllers/v1/action_counters_controller.rb b/app/controllers/v1/action_counters_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e66a4d38d32b6617b5fb502f942c6f2f481e936a
--- /dev/null
+++ b/app/controllers/v1/action_counters_controller.rb
@@ -0,0 +1,43 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class V1::ActionCountersController < ApplicationController
+  include ::Paginator
+
+  before_action :set_action_counter, only: :show
+
+  def index
+    action_counters = paginate ActionCounter
+    render json: action_counters
+  end
+
+  def show
+    render json: @action_counter
+  end
+
+  private
+
+  def set_action_counter
+    @action_counter ||= ActionCounter.find_by_id(params[:id])
+    if @action_counter.blank?
+      render status: :not_found
+    end
+  end
+
+end
diff --git a/app/controllers/v1/actions_controller.rb b/app/controllers/v1/actions_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..daa70d2c1fd7db5bd0ebb093dd337229d85982fb
--- /dev/null
+++ b/app/controllers/v1/actions_controller.rb
@@ -0,0 +1,83 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class V1::ActionsController < ApplicationController
+  include ::Paginator
+
+  before_action :set_action, only: [:show, :update, :destroy]
+  before_action :authenticate_user!, except: [:index, :show]
+  before_action :authorize!, only: [:update, :destroy]
+
+  # GET /v1/actions
+  def index
+    actions = paginate Action
+    render json: actions
+  end
+
+  # GET /v1/actions/1
+  def show
+    render json: @action
+  end
+
+  # POST /v1/actions
+  def create
+    @action = Action.new(action_params)
+    authorize @action
+
+    if @action.save
+      render json: @action, status: :created
+    else
+      render json: @action.errors, status: :unprocessable_entity
+    end
+  end
+
+  # PUT/PATCH /v1/actions/1
+  def update
+    if @action.update(action_params)
+      render json: @action, status: :ok
+    else
+      render json: @action.errors, status: :unprocessable_entity
+    end
+  end
+
+  # DELETE /v1/actions/1
+  def destroy
+    @action.destroy
+    response = { 'status': 'deleted' }
+    render status: :ok, json: response
+  end
+
+  private
+
+  def set_action
+    @action ||= Action.find_by_id(params[:id])
+    if @action.blank?
+      render status: :not_found
+    end
+  end
+
+  def action_params
+    params.require(:action_params).permit(:name, :description, :reward_experience)
+  end
+
+  def authorize!
+    authorize @action
+  end
+
+end
diff --git a/app/controllers/v1/items_controller.rb b/app/controllers/v1/items_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..913f9a2fb25be0ddbf7396108dffa5fccdca9229
--- /dev/null
+++ b/app/controllers/v1/items_controller.rb
@@ -0,0 +1,82 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class V1::ItemsController < ApplicationController
+  include ::Paginator
+  include ::ItemsFilter
+
+  before_action :set_item, only: [:show, :update, :destroy]
+  before_action :authenticate_user!, except: [:index, :show]
+  before_action :authorize!, only: [:update, :destroy]
+
+  def index
+    items = paginate filter_items(policy_scope(Item))
+    render json: items
+  end
+
+  def show
+    render json: @item
+  end
+
+  def create
+    @item = Item.new(item_params)
+    authorize @item
+
+    if @item.save
+      render json: @item, status: :created
+    else
+      render json: @item.errors, status: :unprocessable_entity
+    end
+  end
+
+  # PUT/PATCH /v1/items/1
+  def update
+    if @item.update(item_params)
+      render json: @item, status: :ok
+    else
+      render json: @item.errors, status: :unprocessable_entity
+    end
+  end
+
+  # DELETE /v1/items/1
+  def destroy
+    if @item.update(state: 2)
+      render status: :ok, json: @item
+    else
+      render json: @item.errors, status: :unprocessable_entity
+    end
+  end
+
+  private
+
+  def set_item
+    @item ||= Item.find_by_id(params[:id])
+    if @item.blank?
+      render status: :not_found
+    end
+  end
+
+  def item_params
+    params.require(:item).permit(:name, :price, :discount, :description, :state, :item_type, :image, :achievement_id)
+  end
+
+  def authorize!
+    authorize @item
+  end
+end
diff --git a/app/controllers/v1/progresses_controller.rb b/app/controllers/v1/progresses_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3b672257f54fbebf1919c298f57c44ceadf726b0
--- /dev/null
+++ b/app/controllers/v1/progresses_controller.rb
@@ -0,0 +1,44 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class V1::ProgressesController < ApplicationController
+  include ::Paginator
+
+  before_action :set_progress, only: :show
+  before_action :authenticate_user!, only: :index
+
+	def index
+    progresses = paginate current_user.progresses
+		render json: progresses, each_serializer: ProgressSerializer
+	end
+
+	def show
+		render json: @progress, serializer: ProgressSerializer
+	end
+
+  private
+
+  def set_progress
+    @progress ||= Progress.find_by_id(params[:id])
+    if @progress.blank?
+      render status: :not_found
+    end
+  end
+
+end
diff --git a/app/controllers/v1/requirements_controller.rb b/app/controllers/v1/requirements_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d028b5e955661e4dfc652c561cfea92fb19d6286
--- /dev/null
+++ b/app/controllers/v1/requirements_controller.rb
@@ -0,0 +1,86 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class V1::RequirementsController < ApplicationController
+  include ::Paginator
+
+  before_action :set_requirement, only: [:show, :update, :destroy]
+  before_action :authenticate_user!, except: [:index, :show]
+  before_action :authorize!, only: [:update, :destroy]
+
+	def index
+    requirements = paginate Requirement
+		render json: requirements
+	end
+
+	def show
+    render json: @requirement
+	end
+
+  def create
+    @requirement = Requirement.new(requirement_params)
+    authorize @requirement
+
+    if @requirement.save
+      @requirement.add_achievements(extra_params[:achievements])
+      render json: @requirement, status: :created
+    else
+      render json: @requirement.errors, status: :unprocessable_entity
+    end
+  end
+
+  # PUT/PATCH /v1/requirements/1
+  def update
+    if @requirement.update(requirement_params)
+      @requirement.update_achievements(extra_params[:achievements])
+      render json: @requirement, status: :ok
+    else
+      render json: @requirement.errors, status: :unprocessable_entity
+    end
+  end
+
+  # DELETE /v1/requirements/1
+  def destroy
+    @requirement.destroy
+    response = { 'status': 'deleted' }
+    render status: :ok, json: response
+  end
+
+  private
+
+  def set_requirement
+    @requirement ||= Requirement.find_by_id(params[:id])
+    if @requirement.blank?
+      render status: :not_found
+    end
+  end
+
+  def requirement_params
+    params.require(:requirement).permit(:description, :goal, :repeatable, :action_id)
+  end
+
+  def extra_params
+    return {} if params[:requirement].nil?
+    params[:requirement].permit(achievements: [])
+  end
+
+  def authorize!
+    authorize @requirement
+  end
+end
diff --git a/app/controllers/v1/unlocked_achievements_controller.rb b/app/controllers/v1/unlocked_achievements_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fd09b410adfe70547fe93f77552a8ed5e1d8621e
--- /dev/null
+++ b/app/controllers/v1/unlocked_achievements_controller.rb
@@ -0,0 +1,44 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class V1::UnlockedAchievementsController < ApplicationController
+  include ::Paginator
+  
+  before_action :set_achievement, only: :show
+  before_action :authenticate_user!, only: :index
+
+  def index
+    achievements = paginate current_user.unlocked_achievements
+    render json: achievements
+  end
+
+  def show
+    render json: @achievement
+  end
+
+  private
+
+  def set_achievement
+    @achievement ||= UnlockedAchievement.find_by_id(params[:id])
+    if @achievement.blank?
+      render status: :not_found
+    end
+  end
+
+end
diff --git a/app/controllers/v1/user_items_controller.rb b/app/controllers/v1/user_items_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..32034788219227e79835d42b518a2ab0c48035b1
--- /dev/null
+++ b/app/controllers/v1/user_items_controller.rb
@@ -0,0 +1,45 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class V1::UserItemsController < ApplicationController
+  include ::Paginator
+  include ::ItemsFilter
+  
+  before_action :set_user_item, only: :show
+  before_action :authenticate_user!, only: :index
+
+  def index
+    items = paginate filter_items(current_user.items.active)
+    render json: items
+  end
+
+  def show
+    render json: @user_item
+  end
+
+  private
+
+  def set_user_item
+    @user_item ||= UserItem.find_by_id(params[:id])
+    if @user_item.blank?
+      render status: :not_found
+    end
+  end
+
+end
diff --git a/app/controllers/v1/users_controller.rb b/app/controllers/v1/users_controller.rb
index cfef65445dd3460b651f8663238764fdecc68e96..138c45b5de824b2235d7544ca47c0c839174694c 100644
--- a/app/controllers/v1/users_controller.rb
+++ b/app/controllers/v1/users_controller.rb
@@ -22,15 +22,103 @@ class V1::UsersController < ApplicationController
   include ::DeletedObjectsController
   include ::Paginator
   include ::PublisherController
-	include ::SubjectableController
+  include ::SubjectableController
+  include ::ItemsFilter
 
-  before_action :set_user, only: [:show, :update, :destroy, :following, :own_reviews, :received_reviews, :followers, :add_teacher, :remove_teacher, :reactivate_user]
+  before_action :set_user, only: [:show, :update, :destroy, :following, :own_reviews, :received_reviews, :followers, :add_teacher, :remove_teacher, :reactivate_user, :items]
   before_action :set_new_user, only: [:teacher_requests, :teacher_request]
   before_action :authenticate_user!, only: [:create, :update, :destroy,
                 :following, :own_reviews, :received_reviews, :followers, :create_teacher_request, :update_teacher_request,
                 :teacher_requests, :add_teacher, :remove_teacher, :reactivate_user]
   before_action :authorize_user, only: [:own_reviews, :received_reviews, :update, :destroy, :teacher_requests, :add_teacher, :remove_teacher]
 
+  # gamification
+  # POST /v1/users/complete_action
+  def complete_action
+    # current_user completes an action
+    # action counters and user progresses for achievements are updated,
+    # and if an achievement was conquered its rewards are given
+    quantity = params[:quantity].to_i > 1 ? params[:quantity].to_i : 1
+
+    success, response = current_user.complete_action(Action.where(id: params[:action_id]).first, quantity)
+    status = success ? :ok : :unprocessable_entity
+
+    render json: response, status: status
+  end
+
+  # POST /v1/users/purchase_item
+  def purchase_item
+    # purchases given item for current user
+    item = Item.find_by(id: params[:item_id])
+    user_item = UserItem.where(user: current_user, item: item).first
+    if !user_item.blank?
+      render json: { "error": "You already own this item." }, status: :unprocessable_entity
+    else
+      if current_user.points >= item.price - item.discount
+        current_user.points -= item.price - item.discount
+        if !current_user.save
+          render json: current_user.errors, status: :unprocessable_entity
+        else
+          current_user.add_user_item(item)
+          render json: current_user.user_items, status: :ok
+        end
+      else
+        render json: { "error": "You don't have the required points for this item." }, status: :unprocessable_entity
+      end
+    end
+  end
+
+  # POST /v1/users/equip_item
+  def equip_item
+    if current_user.equip_item(Item.find_by(id: params[:item_id]))
+      render json: current_user.user_items, status: :ok
+    else
+      render json: current_user.errors, status: :unprocessable_entity
+    end
+  end
+
+  # POST /v1/users/unequip_item
+  def unequip_item
+    if current_user.unequip_item(Item.find_by(id: params[:item_id]))
+      render json: current_user.user_items, status: :ok
+    else
+      render json: current_user.errors, status: :unprocessable_entity
+    end
+  end
+
+  # POST /v1/users/remove_item
+  def remove_item
+    if current_user.remove_item(Item.find_by(id: params[:item_id]))
+      render json: current_user.user_items, status: :ok
+    else
+      render json: current_user.errors, status: :unprocessable_entity
+    end
+  end
+
+  # GET /v1/users/action_counters
+  def action_counters
+    # retorna action_counters e contagens correspondentes
+    render json: paginate(current_user.action_counters), status: :ok
+  end
+
+  # GET /v1/users/completed_achievements
+  def completed_achievements
+    render json: paginate(current_user.unlocked_achievements), status: :ok
+  end
+
+  # GET /v1/users/:id/items
+  def items
+    query = {}
+    query[:items] = { item_type: params[:item_type] } if Item.item_types.keys.include? params[:item_type]
+    query[:being_used] = params[:being_used] if ["true", "false"].include? params[:being_used]
+
+    items = @user.user_items.includes(:item).where(query)
+
+    render json: paginate(items), status: :ok
+  end
+#
+# end gamification
+
   # GET /v1/users
   # GET /v1/users.json
   def index
@@ -139,6 +227,7 @@ class V1::UsersController < ApplicationController
         @user.submitter_request = :accepted
         @user.roles << Role.where(name: "publisher")
         if @user.save
+          @user.complete_action(Action.find_by_name("Autenticação de Professor"))
           TeacherMailer.new_teacher_approved(@user).deliver_now
           render status: :ok
         else
@@ -200,6 +289,10 @@ class V1::UsersController < ApplicationController
 
   private
 
+  def xp_to_level(x)
+    (100 + Math.log([x, 900].min, 1.07)).floor
+  end
+
   def deleted_resource
     User
   end
diff --git a/app/models/achievement.rb b/app/models/achievement.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f6175da002c0750d3947f5e770fea91df5cb8e9f
--- /dev/null
+++ b/app/models/achievement.rb
@@ -0,0 +1,76 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+# == Schema Information
+#
+# Table name: achievement
+
+# t.string      :name
+# t.string      :description
+# t.integer     :reward_experience
+# t.integer     :reward_points
+# t.integer     :state 
+# t.integer     :repeatable
+# t.boolean     :resettable
+# t.datetime    :created_at
+# t.datetime    :updated_at
+
+class Achievement < ApplicationRecord
+  has_many   :items
+  has_many   :unlocked_achievements
+  has_many   :users, through: :unlocked_achievements
+  has_and_belongs_to_many :requirements
+
+  enum state: [:inactive, :active, :deleted]
+  enum repeatable: [:never, :daily, :weekly, :monthly, :yearly]
+
+  validates_presence_of :name, :description, :reward_experience, :reward_points, :state, :repeatable
+
+  def add_requirements(ids=[])
+    errors = []
+    ids.each do |requirement_id|
+      if !requirement_ids.include?(requirement_id)
+        requirement = Requirement.where(id: requirement_id).first
+        if !requirement.blank?
+          requirements << requirement
+        else
+          errors << {error_type:"inexistent", id: requirement_id}
+        end
+      else
+        errors << {error_type:"repeated", id: requirement_id}
+      end
+    end
+
+    return errors
+  end
+
+  def remove_requirements(ids=[])
+    ids.each do |requirement_id|
+      requirement = requirements.where(id: requirement_id).first
+      if !requirement.blank?
+        self.requirements.delete(requirement)
+      end
+    end
+  end
+
+  def update_requirements(ids)
+    ids ||= []
+    add_requirements(ids - requirement_ids)
+    remove_requirements(requirement_ids - ids)
+  end
+end
diff --git a/app/models/action.rb b/app/models/action.rb
new file mode 100644
index 0000000000000000000000000000000000000000..33cbf2dfc0fc4325ef215348f19c0cec94fd13e5
--- /dev/null
+++ b/app/models/action.rb
@@ -0,0 +1,35 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+# == Schema Information
+#
+# Table name: action
+
+# t.integer  "name"
+# t.integer  "description"
+# t.datetime "created_at"
+# t.datetime "updated_at"
+# t.integer  "reward_experience"
+
+class Action < ApplicationRecord
+  has_many :requirements
+  has_many :action_counters
+  has_many :users, through: :action_counters
+
+  validates_presence_of :name, :description
+end
diff --git a/app/models/action_counter.rb b/app/models/action_counter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bf221cae4a32d1e6f51db47e0593d8986ba2bf4a
--- /dev/null
+++ b/app/models/action_counter.rb
@@ -0,0 +1,32 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+# == Schema Information
+#
+# Table name: action_counter
+
+# t.belongs_to      :action, index: true
+# t.belongs_to      :user, index:true
+# t.integer         :counter, default: 0
+
+class ActionCounter < ApplicationRecord
+  belongs_to :action
+  belongs_to :user
+
+  validates_presence_of :user, :action
+end
diff --git a/app/models/collection.rb b/app/models/collection.rb
index 1a4d039e32677b0a993810c643d33302ac71e67d..a9a145d26deea2805bfa2992ed663ca823c1ada5 100644
--- a/app/models/collection.rb
+++ b/app/models/collection.rb
@@ -67,6 +67,7 @@ class Collection < ApplicationRecord
   validates :name, :owner, presence: true
   validates_inclusion_of :privacy, in: %w(public private), message: 'Privacy must be public or private'
 
+  after_create :create_action
   before_destroy :delete_index
 
   scope :from_user, ->(user) { where(owner: user) }
@@ -108,7 +109,7 @@ class Collection < ApplicationRecord
   end
 
   def should_index?
-      deleted_at.nil?
+    deleted_at.nil?
   end
 
   def delete_index
@@ -173,4 +174,8 @@ class Collection < ApplicationRecord
   def ignore_changes
     super + %w(score views_count downloads_count likes_count shares_count follows_count)
   end
+
+  def create_action
+    owner.complete_action(Action.find_by_name("Criar Coleção"))
+  end
 end
diff --git a/app/models/collection_item.rb b/app/models/collection_item.rb
index 9b421358dbd0a3d9c447f835c6ef2dc5876b4676..9088f56896955cf4f2ce8d8fcd6ebb0917a2395e 100644
--- a/app/models/collection_item.rb
+++ b/app/models/collection_item.rb
@@ -34,6 +34,8 @@ class CollectionItem < ApplicationRecord
   # *current_user* add item *collectionable* to *collection*
   include Trackable
 
+  after_create :create_action
+
   belongs_to :collection
   belongs_to :collectionable, polymorphic: true
 
@@ -58,4 +60,9 @@ class CollectionItem < ApplicationRecord
     collectionable_type == 'LearningObject' ?  LearningObject.find(collectionable_id).default_thumbnail : Collection.find(collectionable_id).thumbnail
   end
 
+  private
+
+  def create_action
+    collection.owner.complete_action(Action.find_by_name("Adicionar Recurso a Coleção")) if collectionable_type == "LearningObject"
+  end
 end
diff --git a/app/models/concerns/trackable.rb b/app/models/concerns/trackable.rb
index debab1e936ba955ebf07231c6128ab9d8ae58465..97a01079e19e7eb973b82cd52a3f0957ac6ef9f5 100644
--- a/app/models/concerns/trackable.rb
+++ b/app/models/concerns/trackable.rb
@@ -29,9 +29,9 @@ module Trackable
   end
 
   def new_update_activity
-    return nil if previous_changes.blank?
+    return nil if saved_changes.blank?
     return new_activity(:update) if ignore_changes == %w(updated_at)
-    filtered = previous_changes.reject { |x| ignore_changes.include?(x) }
+    filtered = saved_changes.reject { |x| ignore_changes.include?(x.to_s) }
     new_activity(:update) unless filtered.empty?
   end
 
diff --git a/app/models/download.rb b/app/models/download.rb
index 7f7d8d8bc94e2da66b5066b0961ea90606f1d060..6f19bdd7ac04837e22bc93d023044c3eef4de4e6 100644
--- a/app/models/download.rb
+++ b/app/models/download.rb
@@ -33,6 +33,8 @@ class Download < ApplicationRecord
   # *current_user* download *downloadable*
   include Trackable
 
+  after_create :create_action
+
   belongs_to :downloadable, polymorphic: true, counter_cache: true
   belongs_to :user, optional: true
 
@@ -47,4 +49,8 @@ class Download < ApplicationRecord
   def new_create_activity
     new_activity(:create) unless user.nil?
   end
+
+  def create_action
+    user.complete_action(Action.find_by_name("Fazer Download de um Recurso")) if !user.nil? && downloadable_type == "LearningObject"
+  end
 end
diff --git a/app/models/follow.rb b/app/models/follow.rb
index f6236a43e469288f4f44cf63c3f0169855aad91a..cc0d6a13d27cd370eeb2ed662321f778b7f30644 100644
--- a/app/models/follow.rb
+++ b/app/models/follow.rb
@@ -33,6 +33,8 @@ class Follow < ApplicationRecord
   # *current_user* follows *followable*
   include Trackable
 
+  after_create :create_action
+
   belongs_to :followable, polymorphic: true, counter_cache: true
   belongs_to :user
 
@@ -42,4 +44,13 @@ class Follow < ApplicationRecord
   def recipient
     followable
   end
+
+  private
+
+  def create_action
+    if followable_type == "User"
+      user.complete_action(Action.find_by_name("Seguir Usuário"))
+      followable.complete_action(Action.find_by_name("Ser Seguido"))
+    end
+  end
 end
diff --git a/app/models/item.rb b/app/models/item.rb
new file mode 100644
index 0000000000000000000000000000000000000000..51e40f5508cd88b07fe9bbb5df2376ac43334b96
--- /dev/null
+++ b/app/models/item.rb
@@ -0,0 +1,56 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+# == Schema Information
+#
+# Table name: items
+
+# id:                   integer
+# name:                 string
+# price:                integer
+# discount:             integer
+# description:          string
+# state:                integer
+# item_type:            integer
+# image_file_name:      string
+# image_content_type:   string
+# image_file_size:      integer
+# image_updated_at:     datetime
+# created_at:           datetime
+# updated_at:           datetime
+
+class Item < ApplicationRecord
+  belongs_to :achievement, optional: true
+  has_many :user_items
+  has_many :users, through: :user_items
+
+  enum state: [:inactive, :active, :removed]
+  enum item_type: [:avatar_frame, :badge, :card_frame, :cover_frame]
+
+  validates_presence_of :name, :price, :description, :item_type
+
+  # Change to reasonable sizes after the decision by the designers.
+  # Use conditional size for each item_type accordingly to the design decision (https://github.com/thoughtbot/paperclip/wiki/Conditionally-resizing-images).
+  has_attached_file :image, styles: {:thumb => "100x100#", :small  => "150x150>", :medium => "200x200"}
+  validates_attachment_content_type :image, content_type: ['image/jpg', 'image/jpeg', 'image/png', 'image/gif']
+  validates_attachment_presence :image
+
+  def achievable?
+    achievement_id?
+  end
+end
diff --git a/app/models/like.rb b/app/models/like.rb
index 8cd755c0cb480c5cc1a02c1679f34a9dbe3094af..7b1a273890cbde17d0c06740845161ede19c6dff 100644
--- a/app/models/like.rb
+++ b/app/models/like.rb
@@ -34,6 +34,8 @@ class Like < ApplicationRecord
   # *current_user* unlikes *likeable*
   include Trackable
 
+  after_create :create_action
+
   belongs_to :likeable, polymorphic: true, counter_cache: true
   belongs_to :user, counter_cache: true
 
@@ -43,4 +45,10 @@ class Like < ApplicationRecord
   def recipient
     likeable
   end
+
+  private
+
+  def create_action
+    user.complete_action(Action.find_by_name("Favoritar"))
+  end
 end
diff --git a/app/models/progress.rb b/app/models/progress.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8370eb4f98d4cdb88afd8fcee00249e4418ca005
--- /dev/null
+++ b/app/models/progress.rb
@@ -0,0 +1,34 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+# == Schema Information
+#
+# Table name: progress
+
+# t.belongs_to  :user
+# t.belongs_to  :achievement
+# t.belongs_to  :progress_type
+# t.integer     :counter, default: 0
+
+class Progress < ApplicationRecord
+  belongs_to :requirement
+  belongs_to :user
+
+  validates_presence_of :user, :requirement
+  scope :user_progresses, -> (id) { where(user_id: id) }
+end
diff --git a/app/models/requirement.rb b/app/models/requirement.rb
new file mode 100644
index 0000000000000000000000000000000000000000..58d1a41b5c95df284bf16efcbda620e86d38681f
--- /dev/null
+++ b/app/models/requirement.rb
@@ -0,0 +1,75 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+# == Schema Information
+#
+# Table name: progress_type
+
+# t.belongs_to  "action"
+# t.belnogs_to  "achievement"
+# t.string      "description"
+# t.integer     "goal"
+# t.boolean     "repeatable"
+
+# t.datetime    "created_at"
+# t.datetime    "updated_at"
+
+class Requirement < ApplicationRecord
+  belongs_to  :action
+  has_many    :progresses
+  has_many    :users, through: :progresses
+  has_and_belongs_to_many :achievements
+
+  accepts_nested_attributes_for :progresses, allow_destroy: true
+
+  validates_presence_of :goal, :description, :action
+
+  def add_achievements(ids=[])
+    errors = []
+    ids.each do |achievement_id|
+      if !achievement_ids.include?(achievement_id)
+        achievement = Achievement.where(id: achievement_id).first
+        if !achievement.blank?
+          achievements << achievement
+        else
+          errors << {error_type:"inexistent", id: achievement_id}
+        end
+      else
+        errors << {error_type:"repeated", id: achievement_id}
+      end
+    end
+
+    return errors
+  end
+
+  def remove_achievements(ids=[])
+    ids.each do |achievement_id|
+      achievement = achievements.where(id: achievement_id).first
+      if !achievement.blank?
+        self.achievements.delete(achievement)
+      end
+    end
+  end
+
+  def update_achievements(ids)
+    ids ||= []
+    add_achievements(ids - achievement_ids)
+    remove_achievements(achievement_ids - ids)
+  end
+end
diff --git a/app/models/review.rb b/app/models/review.rb
index 9a4d44ba5e838d8e5bc37cce46b9dcc51a50ea92..b1e47ebdabf016803d75eed313317e7cc17314d9 100644
--- a/app/models/review.rb
+++ b/app/models/review.rb
@@ -41,6 +41,7 @@ class Review < ApplicationRecord
 
   after_save :calculate_review_rate
   after_destroy :calculate_review_rate
+  after_create :create_action
 
   belongs_to :reviewable, polymorphic: true
   belongs_to :user
@@ -96,4 +97,9 @@ class Review < ApplicationRecord
     # ReviewAverageCalculatorWorker.perform_in(1.minutes, reviewable.id, reviewable.class.name)
   end
 
+  def create_action
+    user.complete_action(Action.find_by_name("Avaliar"))
+    user.complete_action(Action.find_by_name("Comentar")) if !description.blank?
+  end
+
 end
diff --git a/app/models/role.rb b/app/models/role.rb
index b44c81af046e38d9e22723d3016445fc427a51ab..3a25f8812e181a65d37e3691c3ea6943423ed60b 100644
--- a/app/models/role.rb
+++ b/app/models/role.rb
@@ -31,6 +31,7 @@
 class Role < ApplicationRecord
   has_and_belongs_to_many :users
   has_and_belongs_to_many :emails
+  has_and_belongs_to_many :achievements
 
   validates :name, presence: true, uniqueness: true
 
diff --git a/app/models/unlocked_achievement.rb b/app/models/unlocked_achievement.rb
new file mode 100644
index 0000000000000000000000000000000000000000..84a33349750dea44d2ae5ae6d60c8ea10323a424
--- /dev/null
+++ b/app/models/unlocked_achievement.rb
@@ -0,0 +1,31 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+# == Schema Information
+#
+# Table name: unlocked_achievement
+
+# t.belongs_to      :achievement, index: true
+# t.belongs_to      :user, index:true
+
+class UnlockedAchievement < ApplicationRecord
+  belongs_to :achievement
+  belongs_to :user
+
+  validates_presence_of   :achievement, :user
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index fd7756204827b4690366214de65beea23577ba20..05c0df2fcb1892ab2db2cdcf1d5c4b3f3cb29cdf 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -95,10 +95,23 @@ class User < ApplicationRecord
   has_many :curator_assignments
   has_many :assignments, through: :curator_assignments, source: :submission
 
+  has_many  :user_items
+  has_many  :items, through: :user_items
+
+  has_many  :progresses
+  has_many  :requirements, through: :progresses
+
+  has_many  :unlocked_achievements
+  has_many  :achievements, through: :unlocked_achievements
+
+  has_many  :action_counters
+  has_many  :actions, through: :action_counters
+
   after_create :default_role
   before_save :verify_teacher_id
   after_save :verify_dspace_info
   before_destroy :delete_index
+  after_update :create_actions
 
   has_attached_file :avatar, styles: { medium: '300x300>', thumb: '60x60>' }, default_url: ''
   validates_attachment_content_type :avatar, content_type: ['image/jpg', 'image/jpeg', 'image/png', 'image/gif']
@@ -235,6 +248,132 @@ class User < ApplicationRecord
                   'reviews.reviewable_id = learning_objects.id')
   end
 
+  # ~~~~ gamification actions ~~~~ #
+
+  def add_user_item(item)
+    UserItem.where(user_id: self.id, item_id: item.id).first_or_create
+  end
+
+  def equip_item(item)
+    user_item = add_user_item(item)
+    equipped_items = user_items.includes(:item).where(items: {item_type: item.item_type}, being_used: :true)
+
+    if((item.item_type == "badge" && equipped_items.count == 3) || (item.item_type != "badge" && equipped_items.count > 0))
+      unequip_item(equipped_items.first)
+    end
+    user_item.being_used = true
+    user_item.save
+  end
+
+  def unequip_item(item)
+    user_item = user_items.where(item_id: item.id).first
+    user_item.being_used = false
+    user_item.save
+  end
+
+  def remove_item(item)
+    UserItem.where(user_id: self.id, item_id: item.id).destroy_all
+  end
+
+  def xp_to_level(lvl)
+    # necessary xp to reach level x from level x-1
+    (100 + Math.log([lvl, 900].min, 1.07)).floor
+  end
+
+  def xp_to_next_level
+    # necessary xp for the user to reach next level
+    xp_to_level(level + 1) - experience
+  end
+
+  def calculate_level(xp)
+    necessary_xp = xp_to_level(level + 1)
+    new_level = self.level
+    while xp >= necessary_xp
+      new_level += 1
+      xp -= necessary_xp
+      necessary_xp = xp_to_level(level + 1)
+    end
+    return new_level, xp
+  end
+
+  def experience=(xp)
+    new_level, remaining_xp = calculate_level(xp)
+    self.level = new_level
+    super(remaining_xp)
+  end
+
+  def get_achievement(achievement)
+    return false if !achievement.active?
+
+    unlocked_achievements << UnlockedAchievement.create(user_id: self.id, achievement_id: achievement.id)
+
+    achievement.items.each do |item|
+      add_user_item(item)
+    end
+    
+    self.points += achievement.reward_points
+    self.experience += achievement.reward_experience
+    self.save
+
+    progresses.includes(requirement: :achievements).where(achievements:{ id: achievement.id }).each do |progress|
+      if achievement.repeatable != "never"
+        progress.update(counter: 0)
+      end
+    end
+  end
+
+  def complete_action(action, quantity=1)
+    return false, { "error": "action not found"} if action.blank?
+
+    action_counter = action_counters.where(action: action).first_or_create
+    action_counter.counter += quantity
+
+    if !action_counter.save
+      return false, action_counter.errors
+    else
+      self.update(experience: experience + (action.reward_experience*quantity))
+      action.requirements.each do |requirement|
+        progress = progresses.where(requirement: requirement).first_or_create
+        progress.update(counter: progress.counter + quantity)
+        requirement.achievements.each do |achievement|
+          if progress.counter >= requirement.goal && unlocked_achievements.where(achievement: achievement).blank?
+            incomplete = false
+            achievement_progresses = progresses.includes(requirement: :achievements).where(achievements:{ id: achievement.id })
+            achievement_progresses.each do |progress|
+              incomplete = true if progress.counter < progress.requirement.goal
+            end
+            progress.update(counter: progress.counter - requirement.goal) if requirement.repeatable != "never"
+            get_achievement(achievement) if !incomplete
+          end
+        end
+      end
+      return true, action_counter
+    end
+  end
+
+  def create_actions
+    if saved_changes.include?("current_sign_in_at")
+      months_since_creation = ((Time.now - created_at)/1.month).to_i
+      month_action = Action.find_by_name("Meses de Conta")
+      month_counter = action_counters.where(action: month_action).first_or_create.counter
+
+      complete_action(month_action, months_since_creation - month_counter) if months_since_creation >= 1 && months_since_creation > month_counter
+
+      complete_action(Action.find_by_name("Fazer Login")) if current_sign_in_at.to_date != last_sign_in_day.to_date
+
+      update(last_sign_in_day: current_sign_in_at) if current_sign_in_at.to_date > last_sign_in_day.to_date
+
+    elsif saved_changes.include?("avatar")
+      complete_action(Action.find_by_name("Adicionar Foto de Perfil"))
+    elsif saved_changes.include?("cover")
+      complete_action(Action.find_by_name("Adicionar Capa de Perfil"))
+    elsif saved_changes.include?("description")
+      complete_action(Action.find_by_name("Adicionar Descrição do Usuário"))
+    end
+  end
+
+  # ~~~~ end of gamification actions ~~~~ #
+
   # ~~~~ followable actions ~~~~ #
   # An user can follow anothers users and collections
   # Examples:
diff --git a/app/models/user_item.rb b/app/models/user_item.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c45cc51626457a215f9cb3fdf430ee0b56474af2
--- /dev/null
+++ b/app/models/user_item.rb
@@ -0,0 +1,42 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+# == Schema Information
+#
+# Table name: inventory
+
+# t.belongs_to  :user, index: true
+# t.belongs_to  item, index: true
+# t.boolean     :being_used, default: false
+# t.datetime    "created_at"
+# t.datetime    "updated_at"
+
+class UserItem < ApplicationRecord
+  belongs_to :user
+  belongs_to :item
+
+  validates_presence_of :item, :user
+
+  def unlocked_by_achievement?
+    self.item.achievable?
+  end
+
+  def unlocked_by_purchase?
+    !self.item.achievable?
+  end
+end
diff --git a/app/models/view.rb b/app/models/view.rb
index c6e4f9f1fc93726f5d0c2723080ace7bed326853..c18f1b5cb9927e57e2def31abb15b779dc454d0b 100644
--- a/app/models/view.rb
+++ b/app/models/view.rb
@@ -40,6 +40,7 @@ class View < ApplicationRecord
   validates_presence_of :viewable, :ip
 
   before_create :current_time_greater_than_last
+  after_create :create_action
 
   def recipient
     viewable
@@ -56,4 +57,16 @@ class View < ApplicationRecord
 
     true
   end
+
+  def create_action
+    if !user.nil?
+      if viewable_type == "LearningObject"
+        user.complete_action(Action.find_by_name("Visualizar um Recurso"))
+        viewable.subjects.each do |subject|
+          user.complete_action(Action.find_by_name("Visualizar um Recurso de #{subject.name}"))
+        end
+      end
+      user.complete_action(Action.find_by_name("Visualizar uma Coleção")) if viewable_type == "Collection"
+    end
+  end
 end
diff --git a/app/policies/achievement_policy.rb b/app/policies/achievement_policy.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f00634b01739b78cb54031e5739b3008f907a0e6
--- /dev/null
+++ b/app/policies/achievement_policy.rb
@@ -0,0 +1,42 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class AchievementPolicy < ApplicationPolicy
+
+  def index?
+    record
+  end
+
+  def show?
+    record
+  end
+
+  def create?
+    record if user_exists? && user_can_edit?
+  end
+
+  def update?
+    record if user_exists? && user_can_edit?
+  end
+
+  def destroy?
+    record if user_exists? && user_can_edit?
+  end
+
+end
diff --git a/app/policies/action_policy.rb b/app/policies/action_policy.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fe5440762a6826ff729e63e0cb1801f2949b7199
--- /dev/null
+++ b/app/policies/action_policy.rb
@@ -0,0 +1,41 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class ActionPolicy < ApplicationPolicy
+
+    def index?
+        record 
+    end
+
+    def show?
+        record 
+    end
+
+    def create?
+        record if user_exists? && user_can_edit?
+    end
+
+    def update?
+        record if user_exists? && user_can_edit?
+    end
+
+    def destroy?
+        record if user_exists? && user_can_edit?
+    end
+end
diff --git a/app/policies/item_policy.rb b/app/policies/item_policy.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a70e971b339166468d164abd2c5719d67d565d14
--- /dev/null
+++ b/app/policies/item_policy.rb
@@ -0,0 +1,53 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class ItemPolicy < ApplicationPolicy
+
+  class Scope < Scope
+    def resolve
+      if user.nil?
+        scope.where(state: 'active')
+      elsif user_can_edit?
+        scope.all
+      else
+        scope.where(state: 'active')
+      end
+    end
+  end
+
+  def index?
+    record
+  end
+
+  def show?
+    record
+  end
+
+  def create?
+    record if user_can_edit?
+  end
+
+  def update?
+    record if user_can_edit?
+  end
+
+  def destroy?
+    record if user_can_edit?
+  end
+end
diff --git a/app/policies/requirement_policy.rb b/app/policies/requirement_policy.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bd3823fc7053ac0e11ccfd88bb1eaf9878116b88
--- /dev/null
+++ b/app/policies/requirement_policy.rb
@@ -0,0 +1,42 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class RequirementPolicy < ApplicationPolicy
+
+  def index?
+    record
+  end
+
+  def show?
+    record
+  end
+
+  def create?
+    record if user_exists? && user_can_edit?
+  end
+
+  def update?
+    record if user_exists? && user_can_edit?
+  end
+
+  def destroy?
+    record if user_exists? && user_can_edit?
+  end
+
+end
diff --git a/app/serializers/achievement_serializer.rb b/app/serializers/achievement_serializer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a742ba6ac8b59367a995d138bb7f4964d0549ee5
--- /dev/null
+++ b/app/serializers/achievement_serializer.rb
@@ -0,0 +1,35 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class AchievementSerializer < ActiveModel::Serializer
+  cache key: 'achievement', expires_in: 24.hours
+  
+  attributes \
+    :id,
+    :name,                  
+    :description,          
+    :reward_experience,                
+    :reward_points, 
+    :state,                   
+    :created_at,           
+    :updated_at,
+    :repeatable,
+    :resettable
+
+  has_many :requirements
+end
diff --git a/app/serializers/action_counter_serializer.rb b/app/serializers/action_counter_serializer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ce998ed95ae72213d620eec58b7bff64a8397120
--- /dev/null
+++ b/app/serializers/action_counter_serializer.rb
@@ -0,0 +1,30 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class ActionCounterSerializer < ActiveModel::Serializer
+  cache key: 'action_counter', expires_in: 24.hours
+
+  attributes \
+    :id,
+    :counter,
+    :created_at,
+    :updated_at
+
+  belongs_to :action
+  belongs_to :user
+end
diff --git a/app/serializers/action_serializer.rb b/app/serializers/action_serializer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3755dc36fd7782122a7661e699bbdf10dfcd66f8
--- /dev/null
+++ b/app/serializers/action_serializer.rb
@@ -0,0 +1,31 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class ActionSerializer < ActiveModel::Serializer
+  cache key: 'action', expires_in: 24.hours
+
+  attributes \
+    :id,
+    :name,
+    :description,
+    :created_at,
+    :updated_at
+
+    has_many :requirements
+    has_many :action_counters
+end
diff --git a/app/serializers/item_serializer.rb b/app/serializers/item_serializer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b282ca8b78706161a98579c9322cbae1f6f8b239
--- /dev/null
+++ b/app/serializers/item_serializer.rb
@@ -0,0 +1,34 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class ItemSerializer < ActiveModel::Serializer
+  cache key: 'item', expires_in: 4.hours, except: [:image]
+  
+  attributes \
+    :id,
+    :name,                 
+    :price,                
+    :discount,             
+    :description,          
+    :state,                
+    :item_type, 
+    :achievement,                
+    :image,   
+    :created_at,           
+    :updated_at        
+end
diff --git a/app/serializers/progress_serializer.rb b/app/serializers/progress_serializer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dc908110c3ba128681a40c6eea2a49c83625aafe
--- /dev/null
+++ b/app/serializers/progress_serializer.rb
@@ -0,0 +1,31 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class ProgressSerializer < ActiveModel::Serializer
+    cache key: 'progress', expires_in: 24.hours
+  
+    attributes \
+      :id,
+      :counter,
+      :created_at,
+      :updated_at
+  
+    belongs_to :requirement
+    belongs_to :user
+  end
+  
\ No newline at end of file
diff --git a/app/serializers/requirement_serializer.rb b/app/serializers/requirement_serializer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9fd59a4a62b8f467d7e7dfd98c78a4ca0dc2de7e
--- /dev/null
+++ b/app/serializers/requirement_serializer.rb
@@ -0,0 +1,33 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class RequirementSerializer < ActiveModel::Serializer
+  cache key: 'requirement', expires_in: 24.hours
+  
+  attributes \
+    :id,
+    :description,
+    :goal,
+    :repeatable,
+    :created_at,
+    :updated_at
+
+  belongs_to :action
+  has_many :achievements
+  has_many :progresses
+end
diff --git a/app/serializers/unlocked_achievement_serializer.rb b/app/serializers/unlocked_achievement_serializer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..962877106bee9ea73253a498506c35a180df1264
--- /dev/null
+++ b/app/serializers/unlocked_achievement_serializer.rb
@@ -0,0 +1,26 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class UnlockedAchievementSerializer < ActiveModel::Serializer
+  cache key: 'unlocked_achievement', expires_in: 4.hours
+
+  attributes :id, :created_at, :updated_at
+  belongs_to :achievement
+  belongs_to :user
+end
diff --git a/app/serializers/user_item_serializer.rb b/app/serializers/user_item_serializer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7e5c85ade40274cc684130f42f85444e5818a35d
--- /dev/null
+++ b/app/serializers/user_item_serializer.rb
@@ -0,0 +1,26 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+class UserItemSerializer < ActiveModel::Serializer
+  cache key: 'user_item', expires_in: 4.hours, except: [:being_used]
+
+  attributes :id, :being_used, :created_at, :updated_at
+  belongs_to :item
+  belongs_to :user
+end
diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb
index 630c6aa7307ca6508fb3f4d1f1147efe0c91ef44..7c3ecdca8ec7b244d853dfa293dc30c3c9d04247 100644
--- a/app/serializers/user_serializer.rb
+++ b/app/serializers/user_serializer.rb
@@ -17,7 +17,7 @@
 # along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
 
 class UserSerializer < ActiveModel::Serializer
-  cache key: 'user', expires_in: 4.hours, except: [:email, :complained, :followed ]
+  cache key: 'user', expires_in: 4.hours, except: [:email, :complained, :followed, :level, :level_xp, :experience, :points ]
 
   def complained
     object.complained? current_user
@@ -69,6 +69,16 @@ class UserSerializer < ActiveModel::Serializer
     !current_user.nil? && current_user.is_supervisor?
   end
 
+  def level_xp
+    object.xp_to_level(object.level + 1)
+  end
+
+  def user_items
+    items = object.user_items.where(being_used: true)
+    ActiveModel::SerializableResource.new(items, {scope: current_user, scope_name: :current_user, each_serializer: ::UserItemSerializer}).serializable_hash
+  end
+
+
   attributes \
       :id,
       :email,
@@ -95,7 +105,11 @@ class UserSerializer < ActiveModel::Serializer
       :created_at,
       :updated_at,
       :terms_accepted_at,
-      :state
+      :state,
+      :level,
+      :level_xp,
+      :experience,
+      :points
 
   attribute \
       :times_blocked, if: :is_current_user?
@@ -113,4 +127,5 @@ class UserSerializer < ActiveModel::Serializer
   has_many :subjects
   has_many :roles
   has_many :institutions
+  has_many :user_items
 end
diff --git a/app/services/learning_object_publisher.rb b/app/services/learning_object_publisher.rb
index 830bd7cfd34fa67da3bfd73bb02acca5bf996491..5515fc9541ba0169a08080d77637c9df3ca5d095 100644
--- a/app/services/learning_object_publisher.rb
+++ b/app/services/learning_object_publisher.rb
@@ -52,6 +52,7 @@ class LearningObjectPublisher
     learning_object.published_at = Time.current
     learning_object.save
     learning_object.new_activity(:publish)
+    learning_object.publisher.complete_action(Action.find_by_name("Publicar"))
     if learning_object.link? && learning_object.thumbnail.blank?
       att = learning_object.attachments.create(retrieve_link: learning_object.link, bundle_name: "LINK")
       ThumbnailGenerateWorker.perform_async(att.id)
diff --git a/app/services/search_service/collection.rb b/app/services/search_service/collection.rb
index 0106b3a0c358b0ddef30e02b631231a93e3c1ada..3ce50430c0fd10402a6e487942abd582335497ea 100644
--- a/app/services/search_service/collection.rb
+++ b/app/services/search_service/collection.rb
@@ -24,7 +24,7 @@ module SearchService
     end
 
     def autocomplete
-      params = {  where: { privacy: 'public' },
+      params = {  where: { privacy: 'public', empty: false },
                   fields: ['name^10', 'description', 'owner'] }
       result = ::Collection.search(@search.query, autocomplete_params.merge(params))
 
diff --git a/config/initializers/rspec_api_docs.rb b/config/initializers/rspec_api_docs.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c938e0781617aa19630ae2691bd8cc6e99f4f69a
--- /dev/null
+++ b/config/initializers/rspec_api_docs.rb
@@ -0,0 +1,28 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+if !Rails.env.production?
+  module RspecApiDocumentation
+    class RackTestClient < ClientBase
+      def response_body
+        last_response.body.encode("utf-8")
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index eff4781f2380b2603090ab0e88ff0d1988c7910a..d8d97c45cd6ecedd6a68d62fd1a1e209837a7f69 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -129,11 +129,18 @@ Rails.application.routes.draw do
   end
 
   namespace :v1 do
+    resources :unlocked_achievements, :user_items, :progresses, \
+      :action_counters, only: [:index, :show]
+    resources :achievements
+    resources :items
+    resources :actions
+    resources :requirements
+    
     resources :activities, only: [:index, :show] do
-        collection do
-            get 'me'
-            post 'view'
-        end
+      collection do
+        get 'me'
+        post 'view'
+      end
     end
     resources :feed, only: [:index]
 
@@ -148,11 +155,21 @@ Rails.application.routes.draw do
         put 'reactivate_user', to: 'users#reactivate_user'
         post 'add_teacher', to: 'users#add_teacher'
         delete 'remove_teacher', to: 'users#remove_teacher'
+        get 'items'
       end
       collection do
         get 'teacher_requests'
         post 'teacher_request', to: 'users#create_teacher_request'
         put 'teacher_request', to: 'users#update_teacher_request'
+        # Gamefication actions
+        post 'complete_action'
+        post 'purchase_item'
+        post 'equip_item'
+        post 'unequip_item'
+        post 'remove_item'
+        get 'update_level'
+        get 'action_counters'
+        get 'completed_achievements'
       end
     end
 
diff --git a/db/migrate/20210129141847_create_items.rb b/db/migrate/20210129141847_create_items.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ac745c7ebf60abb5c302855859a9d0ebdcc1234b
--- /dev/null
+++ b/db/migrate/20210129141847_create_items.rb
@@ -0,0 +1,15 @@
+class CreateItems < ActiveRecord::Migration[6.0]
+  def change
+    create_table :items do |t|
+      t.string :name
+      t.integer :price
+      t.integer :discount, default: 0
+      t.text :description
+      t.integer :state, default: 0
+      t.integer :item_type
+      t.attachment :image
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20210129143448_create_achievements.rb b/db/migrate/20210129143448_create_achievements.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1e47ff1a418f14cef8e9fa67e2d36f8050216ba2
--- /dev/null
+++ b/db/migrate/20210129143448_create_achievements.rb
@@ -0,0 +1,15 @@
+class CreateAchievements < ActiveRecord::Migration[6.0]
+  def change
+    create_table :achievements do |t|
+      t.string :name
+      t.text :description
+      t.integer :reward_experience
+      t.integer :reward_points
+      t.integer :state
+      t.integer :repeatable
+      t.boolean :resettable
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20210129143709_create_actions.rb b/db/migrate/20210129143709_create_actions.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5f452e504587bde0ccd8cfa638bd0aa3aacf5d7d
--- /dev/null
+++ b/db/migrate/20210129143709_create_actions.rb
@@ -0,0 +1,11 @@
+class CreateActions < ActiveRecord::Migration[6.0]
+  def change
+    create_table :actions do |t|
+      t.string :name
+      t.text :description
+      t.integer :reward_experience
+      
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20210129144002_create_requirements.rb b/db/migrate/20210129144002_create_requirements.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0bf30757b4ec6c7bc4197343193d1e72059574c2
--- /dev/null
+++ b/db/migrate/20210129144002_create_requirements.rb
@@ -0,0 +1,11 @@
+class CreateRequirements < ActiveRecord::Migration[6.0]
+  def change
+    create_table :requirements do |t|
+      t.text :description
+      t.integer :goal
+      t.integer :repeatable
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20210129144219_add_level_to_users.rb b/db/migrate/20210129144219_add_level_to_users.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b79234193bfaa344b64ff057ffcf0de7bb108346
--- /dev/null
+++ b/db/migrate/20210129144219_add_level_to_users.rb
@@ -0,0 +1,7 @@
+class AddLevelToUsers < ActiveRecord::Migration[6.0]
+  def change
+    add_column :users, :experience, :integer, default: 0
+    add_column :users, :level, :integer, default: 1
+    add_column :users, :points, :integer, default: 0
+  end
+end
diff --git a/db/migrate/20210129144650_create_user_items.rb b/db/migrate/20210129144650_create_user_items.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cbed350e409e2e2ee74d247d29d65b06fd137577
--- /dev/null
+++ b/db/migrate/20210129144650_create_user_items.rb
@@ -0,0 +1,12 @@
+class CreateUserItems < ActiveRecord::Migration[6.0]
+  def change
+    create_table :user_items do |t|
+      t.references :item, foreign_key: true
+      t.references :user, foreign_key: true
+      t.boolean :being_used, default: false
+
+      t.timestamps
+    end
+    add_index :user_items, [:user_id, :item_id], unique: true, name: 'user_item_index'
+  end
+end
diff --git a/db/migrate/20210129144819_create_progresses.rb b/db/migrate/20210129144819_create_progresses.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c0b19bda1c199796385d72588020f50ea68cf71a
--- /dev/null
+++ b/db/migrate/20210129144819_create_progresses.rb
@@ -0,0 +1,12 @@
+class CreateProgresses < ActiveRecord::Migration[6.0]
+  def change
+    create_table :progresses do |t|
+      t.references :user, foreign_key: true
+      t.references :requirement, foreign_key: true
+      t.integer :counter, default: 0
+
+      t.timestamps
+    end
+    add_index :progresses, [:user_id, :requirement_id], unique: true, name: 'user_requirement_index'
+  end
+end
diff --git a/db/migrate/20210129145903_create_action_counters.rb b/db/migrate/20210129145903_create_action_counters.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fc82411f9a1994969032a010c29eb43eea516fe6
--- /dev/null
+++ b/db/migrate/20210129145903_create_action_counters.rb
@@ -0,0 +1,12 @@
+class CreateActionCounters < ActiveRecord::Migration[6.0]
+  def change
+    create_table :action_counters do |t|
+      t.references :user, foreign_key: true
+      t.references :action, foreign_key: true
+      t.integer :counter, default: 0
+
+      t.timestamps
+    end
+    add_index :action_counters, [:user_id, :action_id], unique: true, name: 'user_action_index'
+  end
+end
diff --git a/db/migrate/20210129150315_create_unlocked_achievements.rb b/db/migrate/20210129150315_create_unlocked_achievements.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2ea402ab57a9d843f96af98a3b78c5b0579440ab
--- /dev/null
+++ b/db/migrate/20210129150315_create_unlocked_achievements.rb
@@ -0,0 +1,10 @@
+class CreateUnlockedAchievements < ActiveRecord::Migration[6.0]
+  def change
+    create_table :unlocked_achievements do |t|
+      t.references :user, foreign_key: true
+      t.references :achievement, foreign_key: true
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20210129150529_create_join_table_achievement_requirement.rb b/db/migrate/20210129150529_create_join_table_achievement_requirement.rb
new file mode 100644
index 0000000000000000000000000000000000000000..459f73d4374f189a41a70a1989f7415b8e275d19
--- /dev/null
+++ b/db/migrate/20210129150529_create_join_table_achievement_requirement.rb
@@ -0,0 +1,8 @@
+class CreateJoinTableAchievementRequirement < ActiveRecord::Migration[6.0]
+  def change
+    create_join_table :achievements, :requirements do |t|
+      t.timestamps
+    end
+    add_index :achievements_requirements, [:achievement_id, :requirement_id], unique: true, name: 'achievement_requirement_index'
+  end
+end
diff --git a/db/migrate/20210129151023_add_achievement_to_items.rb b/db/migrate/20210129151023_add_achievement_to_items.rb
new file mode 100644
index 0000000000000000000000000000000000000000..92280802e0c43ebeb83fdd03616a6c6cea2fba7c
--- /dev/null
+++ b/db/migrate/20210129151023_add_achievement_to_items.rb
@@ -0,0 +1,5 @@
+class AddAchievementToItems < ActiveRecord::Migration[6.0]
+  def change
+    add_reference :items, :achievement, foreign_key: true
+  end
+end
diff --git a/db/migrate/20210129151109_add_action_to_requirements.rb b/db/migrate/20210129151109_add_action_to_requirements.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3f6c0275bca96df37df10bc5532f10d5bd3bb592
--- /dev/null
+++ b/db/migrate/20210129151109_add_action_to_requirements.rb
@@ -0,0 +1,5 @@
+class AddActionToRequirements < ActiveRecord::Migration[6.0]
+  def change
+    add_reference :requirements, :action, foreign_key: true
+  end
+end
diff --git a/db/migrate/20210423135443_add_last_sign_in_day_to_user.rb b/db/migrate/20210423135443_add_last_sign_in_day_to_user.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a037c8d7cb16bee24c6db77adc5279e875699b11
--- /dev/null
+++ b/db/migrate/20210423135443_add_last_sign_in_day_to_user.rb
@@ -0,0 +1,5 @@
+class AddLastSignInDayToUser < ActiveRecord::Migration[6.0]
+  def change
+    add_column :users, :last_sign_in_day, :datetime, default: -> { 'CURRENT_TIMESTAMP' }
+  end
+end
diff --git a/db/seeds.rb b/db/seeds.rb
index 7b991bd611c001ac58647ac40922323703191f51..ccad4b9d6d902e55ac3f3dc79ad430a5a86881a7 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -24,16 +24,16 @@
 #
 #   cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
 #   Mayor.create(name: 'Emanuel', city: cities.first)
-Role.create(name: 'teacher', description: 'This role represents a Teacher in Portal MEC.')
-Role.create(name: 'student', description: 'This role represents a Student in Portal MEC.')
-Role.create(name: 'admin', description: 'This role represents an MEC Admin, that can perform any action.')
-Role.create(name: 'curator', description: 'This role represents a content Curator in Portal MEC.')
-Role.create(name: 'moderator', description: 'This role represents a content Moderator in Portal MEC, with less privileges than admin.')
-Role.create(name: 'supervisor', description: 'This role represents an user Supervisor in Portal MEC.')
-Role.create(name: 'editor', description: 'This role represents a content Supervisor in Portal MEC, with less privileges than admin.')
-Role.create(name: 'submitter', description: 'This role represents a content submitter in Portal MEC.')
-Role.create(name: 'partner', description: 'This role represents a partner Portal MEC.')
-Role.create(name: 'publisher', description: 'This role represents a content publisher without supervision in Portal MEC.')
+# Role.where(name: 'teacher', description: 'This role represents a Teacher in Portal MEC.').first_or_create
+# Role.where(name: 'student', description: 'This role represents a Student in Portal MEC.').first_or_create
+Role.where(name: 'admin', description: 'This role represents an MEC Admin, that can perform any action.').first_or_create
+Role.where(name: 'curator', description: 'This role represents a content Curator in Portal MEC.').first_or_create
+Role.where(name: 'moderator', description: 'This role represents a content Moderator in Portal MEC, with less privileges than admin.').first_or_create
+Role.where(name: 'supervisor', description: 'This role represents an user Supervisor in Portal MEC.').first_or_create
+Role.where(name: 'editor', description: 'This role represents a content Supervisor in Portal MEC, with less privileges than admin.').first_or_create
+Role.where(name: 'submitter', description: 'This role represents a content submitter in Portal MEC.').first_or_create
+Role.where(name: 'partner', description: 'This role represents a partner Portal MEC.').first_or_create
+Role.where(name: 'publisher', description: 'This role represents a content publisher without supervision in Portal MEC.').first_or_create
 
 # create the default admin
 User.create(
@@ -53,3 +53,8 @@ require_relative 'seeds/ratings'
 require_relative 'seeds/scores'
 require_relative 'seeds/subjects'
 require_relative 'seeds/educational_stages'
+
+require_relative 'seeds/actions'
+require_relative 'seeds/achievements'
+require_relative 'seeds/requirements'
+require_relative 'seeds/items'
diff --git a/db/seeds/achievements.rb b/db/seeds/achievements.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3bd95b8a8e1e535ccacb62ec1724f62dc97c7ccc
--- /dev/null
+++ b/db/seeds/achievements.rb
@@ -0,0 +1,101 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+def achievements
+  main_achievements = [
+    { name: "Introduzido Nível I", description: "Assistir ao vídeo de apresentação na plataforma", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Introduzido Nível II", description: "Completar atividades básicas na plataforma", reward_experience: 75, reward_points: 50, state: 1, repeatable: 0, resettable: false },
+
+    { name: "Personalizado", description: "Personalizar seu perfil", reward_experience: 75, reward_points: 50, state: 1, repeatable: 0, resettable: false },
+
+    { name: "Professor", description: "Ser autenticado como professor", reward_experience: 75, reward_points: 100, state: 1, repeatable: 0, resettable: false },
+
+    { name: "Frequente Nível I", description: "Acessar a plataforma por 5 dias consecutivos", reward_experience: 75, reward_points: 50, state: 1, repeatable: 0, resettable: true },
+    { name: "Frequente Nível II", description: "Acessar a plataforma por 15 dias consecutivos", reward_experience: 75, reward_points: 50, state: 1, repeatable: 0, resettable: true },
+    { name: "Frequente Nível III", description: "Acessar a plataforma por 60 dias consecutivos", reward_experience: 75, reward_points: 50, state: 1, repeatable: 0, resettable: true },
+    { name: "Frequente Nível IV", description: "Acessar a plataforma por 120 dias consecutivos", reward_experience: 75, reward_points: 50, state: 1, repeatable: 0, resettable: true },
+    { name: "Frequente Nível V", description: "Acessar a plataforma por 365 dias consecutivos", reward_experience: 75, reward_points: 100, state: 1, repeatable: 0, resettable: true },
+
+    { name: "Nostálgico Nível I", description: "Usuário há 6 meses", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Nostálgico Nível II", description: "Usuário há 1 ano", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Nostálgico Nível III", description: "Usuário há 2 anos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Nostálgico Nível IV", description: "Usuário há 3 anos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Nostálgico Nível V", description: "Usuário há 4 anos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+
+    { name: "Contribuidor Nível I", description: "Publicar 1 recurso", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Contribuidor Nível II", description: "Publicar 10 recursos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Contribuidor Nível III", description: "Publicar 50 recursos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Contribuidor Nível IV", description: "Publicar 200 recursos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Contribuidor Nível V", description: "Publicar 1000 recursos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+
+    { name: "Dias Produtivos Nível I", description: "Publicar 2 recursos em um dia", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: true },
+    { name: "Dias Produtivos Nível II", description: "Publicar 5 recursos em um dia", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: true },
+    { name: "Dias Produtivos Nível III", description: "Publicar 10 recursos em um dia", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: true },
+    { name: "Dias Produtivos Nível IV", description: "Publicar 20 recursos em um dia", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: true },
+    { name: "Dias Produtivos Nível V", description: "Publicar 50 recursos em um dia", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: true },
+
+    { name: "Utilizador Assíduo Nível I", description: "Utilizar 10 recursos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Utilizador Assíduo Nível II", description: "Utilizar 50 recursos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Utilizador Assíduo Nível III", description: "Utilizar 200 recursos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Utilizador Assíduo Nível IV", description: "Utilizar 1000 recursos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Utilizador Assíduo Nível V", description: "Utilizar 5000 recursos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+
+    { name: "Colecionador Nível I", description: "Utilizar 5 coleções", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Colecionador Nível II", description: "Utilizar 30 coleções", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Colecionador Nível III", description: "Utilizar 100 coleções", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Colecionador Nível IV", description: "Utilizar 1000 coleções", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+
+    { name: "Curador Nível I", description: "Avaliar 20 recursos com estrelas e comentários", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Curador Nível II", description: "Avaliar 200 recursos com estrelas e comentários", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Curador Nível III", description: "Avaliar 500 recursos com estrelas e comentários", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Curador Nível IV", description: "Avaliar 1000 recursos com estrelas e comentários", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    # { name: "Curador Nível V", description: "Avaliar 5000 recursos com estrelas e comentários", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+
+    { name: "Curador Assíduo Nível I", description: "Avaliar 10 recursos com estrelas e comentários em um dia", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: true },
+    { name: "Curador Assíduo Nível II", description: "Avaliar 20 recursos com estrelas e comentários em um dia", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: true },
+    { name: "Curador Assíduo Nível III", description: "Avaliar 50 recursos com estrelas e comentários em um dia", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: true },
+
+    { name: "Conectado Nível I", description: "Seguir 10 usuários", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Conectado Nível II", description: "Seguir 50 usuários", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Conectado Nível III", description: "Seguir 200 usuários", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+
+    { name: "Conhecido Nível I", description: "Ter 10 seguidores", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Conhecido Nível II", description: "Ter 50 seguidores", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Conhecido Nível III", description: "Ter 200 seguidores", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+
+    { name: "Divulgador Nível I", description: "Compartilhar 1 recurso", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Divulgador Nível II", description: "Compartilhar 20 recursos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false },
+    { name: "Divulgador Nível III", description: "Compartilhar 100 recursos", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false }
+  ]
+  subject_achievements = []
+  Subject.all.each do |subject|
+    subject_achievements << { name: "Proficiência em #{subject.name} Nível I", description: "Utilizar 20 recursos de #{subject.name}", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false }
+    subject_achievements << { name: "Proficiência em #{subject.name} Nível II", description: "Utilizar 50 recursos de #{subject.name}", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false }
+    subject_achievements << { name: "Proficiência em #{subject.name} Nível III", description: "Utilizar 150 recursos de #{subject.name}", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false }
+    subject_achievements << { name: "Proficiência em #{subject.name} Nível IV", description: "Utilizar 500 recursos de #{subject.name}", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false }
+    subject_achievements << { name: "Proficiência em #{subject.name} Nível V", description: "Utilizar 1000 recursos de #{subject.name}", reward_experience: 75, reward_points: 10, state: 1, repeatable: 0, resettable: false }
+  end
+  main_achievements + subject_achievements
+end
+
+# xp 0 - 75
+
+achievements.each do |achievement|
+  Achievement.where(achievement).first_or_create
+end
diff --git a/db/seeds/actions.rb b/db/seeds/actions.rb
new file mode 100644
index 0000000000000000000000000000000000000000..46c040a34973c597b429a6fa3a0cc16959142bb8
--- /dev/null
+++ b/db/seeds/actions.rb
@@ -0,0 +1,57 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+def actions
+  main_actions = [
+    {name: "Publicar", description: "Publicar um recurso na plataforma", reward_experience: 50},
+    {name: "Favoritar", description: "Favoritar um recurso na plataforma", reward_experience: 1},
+    {name: "Avaliar", description: "Avaliar um recurso na plataforma", reward_experience: 5},
+    {name: "Comentar", description: "Comentar um recurso na plataforma", reward_experience: 5},
+    {name: "Seguir Usuário", description: "Seguir um usuário na plataforma", reward_experience: 2},
+    {name: "Ser Seguido", description: "Ser seguido por um usuário na plataforma", reward_experience: 10},
+    {name: "Fazer Download de um Recurso", description: "Fazer download de um recurso na plataforma", reward_experience: 2},
+
+    # {name: "Curadoria de Recurso", description: "Realizar a curadoria de um recurso na plataforma", reward_experience: 5},
+
+    {name: "Fazer Login", description: "Fazer login na plataforma em um dia", reward_experience: 0},
+    {name: "Meses de Conta", description: "Ter a conta por um mês na plataforma", reward_experience: 0},
+    {name: "Visualizar um Recurso", description: "Visualizar um recurso na plataforma", reward_experience: 0},
+    {name: "Visualizar uma Coleção", description: "Visualizar uma coleção na plataforma", reward_experience: 0},
+    {name: "Criar Coleção", description: "Criar uma coleção na plataforma", reward_experience: 0},
+    {name: "Adicionar Recurso a Coleção", description: "Adicionar um recurso a uma coleção na plataforma", reward_experience: 0},
+    {name: "Adicionar Foto de Perfil", description: "Adicionar uma foto de perfil na sua conta na plataforma", reward_experience: 0},
+    {name: "Adicionar Capa de Perfil", description: "Adicionar uma capa de perfil na sua conta na plataforma", reward_experience: 0},
+    {name: "Adicionar Descrição do Usuário", description: "Adicionar uma descrição na sua conta na plataforma", reward_experience: 0},
+    {name: "Autenticação de Professor", description: "Ser autenticado como professor na plataforma", reward_experience: 0},
+
+    {name: "Assistir Apresentação", description: "Assistir ao vídeo de apresentação na plataforma", reward_experience: 0},
+    {name: "Visualizar um Material", description: "Visualizar um material de formação na plataforma", reward_experience: 0},
+    {name: "Compartilhar", description: "Compartilhar um recurso na plataforma", reward_experience: 0}
+  ]
+
+  subject_actions = []
+  Subject.all.each do |subject|
+    subject_actions << {name: "Visualizar um Recurso de #{subject.name}", description: "Visualizar um recurso da área de #{subject.name} na plataforma", reward_experience: 0}
+  end
+  main_actions + subject_actions
+end
+
+actions.each do |action|
+  Action.where(action).first_or_create
+end
diff --git a/db/seeds/items.rb b/db/seeds/items.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4053c035f09a3c7c642fca9b9fb393d9b831beb4
--- /dev/null
+++ b/db/seeds/items.rb
@@ -0,0 +1,33 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+def item_data(achievement)
+  { name: achievement.name, price: 0, discount: 0, description: "Prêmio por desbloquear a conquista #{achievement.name}", state: 1, item_type: 1, achievement_id: achievement.id}
+end
+
+Achievement.all.each do |achievement|
+  file_path = Rails.root.join('db','seeds','assets','images','items', achievement.name.parameterize.underscore + '.png')
+  if File.file?(file_path)
+    item = Item.where(item_data(achievement)).first_or_create
+    if item.image.blank?
+      item.image = File.open(file_path)
+      item.save
+    end
+  end
+end
diff --git a/db/seeds/requirements.rb b/db/seeds/requirements.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bef6dc1b965b01c3757cbfb6c51438063e38c8d6
--- /dev/null
+++ b/db/seeds/requirements.rb
@@ -0,0 +1,131 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+def get_action(name)
+  Action.where(name: name).first
+end
+
+def get_achievement(name)
+  Achievement.where(name: name).first
+end
+
+def requirements
+  main_requirements = {
+    "Introduzido Nível I": [{ goal: 1, description: "Assistir ao vídeo de apresentação na plataforma", repeatable: false, action: get_action("Assistir Apresentação") }],
+    
+    "Introduzido Nível II": [
+      { goal: 1, description: "Utilizar um recurso", repeatable: false, action: get_action("Visualizar um Recurso") },
+      { goal: 1, description: "Criar uma coleção privada", repeatable: false, action: get_action("Criar Coleção") },
+      { goal: 1, description: "Utilizar um material", repeatable: false, action: get_action("Visualizar um Material") },
+      { goal: 1, description: "Marcar um item como favorito", repeatable: false, action: get_action("Favoritar") },
+      { goal: 1, description: "Avaliar um item", repeatable: false, action: get_action("Avaliar") },
+      { goal: 1, description: "Seguir um usuário", repeatable: false, action: get_action("Seguir Usuário") },
+      { goal: 1, description: "Salvar um item", repeatable: false, action: get_action("Adicionar Recurso a Coleção") }
+    ],
+
+    "Personalizado": [
+      { goal: 1, description: "Adicionar foto de perfil", repeatable: false, action: get_action("Adicionar Foto de Perfil") },
+      { goal: 1, description: "Adicionar capa de perfil", repeatable: false, action: get_action("Adicionar Capa de Perfil") },
+      { goal: 1, description: "Adicionar 'Sobre Mim'", repeatable: false, action: get_action("Adicionar Descrição do Usuário") },
+    ],
+
+    "Professor": [{ goal: 1, description: "Ser autenticado como professor", repeatable: false, action: get_action("Autenticação de Professor") }],
+    
+    "Frequente Nível I": [{ goal: 5, description: "Acessar a plataforma por 5 dias consecutivos", repeatable: false, action: get_action("Fazer Login") }],
+    "Frequente Nível II": [{ goal: 15, description: "Acessar a plataforma por 15 dias consecutivos", repeatable: false, action: get_action("Fazer Login") }],
+    "Frequente Nível III": [{ goal: 60, description: "Acessar a plataforma por 60 dias consecutivos", repeatable: false, action: get_action("Fazer Login") }],
+    "Frequente Nível IV": [{ goal: 120, description: "Acessar a plataforma por 120 dias consecutivos", repeatable: false, action: get_action("Fazer Login") }],
+    "Frequente Nível V": [{ goal: 365, description: "Acessar a plataforma por 365 dias consecutivos", repeatable: false, action: get_action("Fazer Login") }],
+
+    "Nostálgico Nível I": [{ goal: 6, description: "Usuário há 6 meses", repeatable: false, action: get_action("Meses de Conta") }],
+    "Nostálgico Nível II": [{ goal: 12, description: "Usuário há 1 ano", repeatable: false, action: get_action("Meses de Conta") }],
+    "Nostálgico Nível III": [{ goal: 24, description: "Usuário há 2 anos", repeatable: false, action: get_action("Meses de Conta") }],
+    "Nostálgico Nível IV": [{ goal: 36, description: "Usuário há 3 anos", repeatable: false, action: get_action("Meses de Conta") }],
+    "Nostálgico Nível V": [{ goal: 48, description: "Usuário há 4 anos", repeatable: false, action: get_action("Meses de Conta") }],
+
+    "Contribuidor Nível I": [{ goal: 1, description: "Publicar 1 recurso", repeatable: false, action: get_action("Publicar") }],
+    "Contribuidor Nível II": [{ goal: 10, description: "Publicar 10 recursos", repeatable: false, action: get_action("Publicar") }],
+    "Contribuidor Nível III": [{ goal: 50, description: "Publicar 50 recursos", repeatable: false, action: get_action("Publicar") }],
+    "Contribuidor Nível IV": [{ goal: 200, description: "Publicar 200 recursos", repeatable: false, action: get_action("Publicar") }],
+    "Contribuidor Nível V": [{ goal: 1000, description: "Publicar 1000 recursos", repeatable: false, action: get_action("Publicar") }],
+
+    "Dias Produtivos Nível I": [{ goal: 2, description: "Publicar 2 recursos em um dia", repeatable: false, action: get_action("Publicar") }],
+    "Dias Produtivos Nível II": [{ goal: 5, description: "Publicar 5 recursos em um dia", repeatable: false, action: get_action("Publicar") }],
+    "Dias Produtivos Nível III": [{ goal: 10, description: "Publicar 10 recursos em um dia", repeatable: false, action: get_action("Publicar") }],
+    "Dias Produtivos Nível IV": [{ goal: 20, description: "Publicar 20 recursos em um dia", repeatable: false, action: get_action("Publicar") }],
+    "Dias Produtivos Nível V": [{ goal: 50, description: "Publicar 50 recursos em um dia", repeatable: false, action: get_action("Publicar") }],
+
+    "Utilizador Assíduo Nível I": [{ goal: 10, description: "Utilizar 10 recursos", repeatable: false, action: get_action("Visualizar um Recurso") }],
+    "Utilizador Assíduo Nível II": [{ goal: 50, description: "Utilizar 50 recursos", repeatable: false, action: get_action("Visualizar um Recurso") }],
+    "Utilizador Assíduo Nível III": [{ goal: 200, description: "Utilizar 200 recursos", repeatable: false, action: get_action("Visualizar um Recurso") }],
+    "Utilizador Assíduo Nível IV": [{ goal: 1000, description: "Utilizar 1000 recursos", repeatable: false, action: get_action("Visualizar um Recurso") }],
+    "Utilizador Assíduo Nível V": [{ goal: 5000, description: "Utilizar 5000 recursos", repeatable: false, action: get_action("Visualizar um Recurso") }],
+
+    "Colecionador Nível I": [{ goal: 5, description: "Utilizar 5 coleções", repeatable: false, action: get_action("Visualizar uma Coleção") }],
+    "Colecionador Nível II": [{ goal: 30, description: "Utilizar 30 coleções", repeatable: false, action: get_action("Visualizar uma Coleção") }],
+    "Colecionador Nível III": [{ goal: 100, description: "Utilizar 100 coleções", repeatable: false, action: get_action("Visualizar uma Coleção") }],
+    "Colecionador Nível IV": [{ goal: 1000, description: "Utilizar 1000 coleções", repeatable: false, action: get_action("Visualizar uma Coleção") }],
+
+    "Curador Nível I": [{ goal: 20, description: "Avaliar 20 recursos com estrelas", repeatable: false, action: get_action("Avaliar") },
+      { goal: 20, description: "Avaliar 20 recursos com comentários", repeatable: false, action: get_action("Comentar") }],
+    "Curador Nível II": [{ goal: 200, description: "Avaliar 200 recursos com estrelas", repeatable: false, action: get_action("Avaliar") },
+      { goal: 200, description: "Avaliar 200 recursos com comentários", repeatable: false, action: get_action("Comentar") }],
+    "Curador Nível III": [{ goal: 500, description: "Avaliar 500 recursos com estrelas", repeatable: false, action: get_action("Avaliar") },
+      { goal: 500, description: "Avaliar 500 recursos com comentários", repeatable: false, action: get_action("Comentar") }],
+    "Curador Nível IV": [{ goal: 1000, description: "Avaliar 1000 recursos com estrelas", repeatable: false, action: get_action("Avaliar") },
+      { goal: 1000, description: "Avaliar 1000 recursos com comentários", repeatable: false, action: get_action("Comentar") }],
+    # "Curador Nível V": [{ goal: 5000, description: "Avaliar 5000 recursos com estrelas", repeatable: false, action: get_action("Avaliar") },
+    #   { goal: 5000, description: "Avaliar 5000 recursos com comentários", repeatable: false, action: get_action("Comentar") }],
+
+    "Curador Assíduo Nível I": [{ goal: 10, description: "Avaliar 10 recursos com estrelas em um dia", repeatable: false, action: get_action("Avaliar") },
+      { goal: 10, description: "Avaliar 10 recursos com comentários em um dia", repeatable: false, action: get_action("Comentar") }],
+    "Curador Assíduo Nível II": [{ goal: 20, description: "Avaliar 20 recursos com estrelas em um dia", repeatable: false, action: get_action("Avaliar") },
+      { goal: 20, description: "Avaliar 20 recursos com comentários em um dia", repeatable: false, action: get_action("Comentar") }],
+    "Curador Assíduo Nível III": [{ goal: 50, description: "Avaliar 50 recursos com estrelas em um dia", repeatable: false, action: get_action("Avaliar") },
+      { goal: 50, description: "Avaliar 50 recursos com comentários em um dia", repeatable: false, action: get_action("Comentar") }],
+
+    "Conectado Nível I": [{ goal: 10, description: "Seguir 10 usuários", repeatable: false, action: get_action("Seguir Usuário") }],
+    "Conectado Nível II": [{ goal: 50, description: "Seguir 50 usuários", repeatable: false, action: get_action("Seguir Usuário") }],
+    "Conectado Nível III": [{ goal: 200, description: "Seguir 200 usuários", repeatable: false, action: get_action("Seguir Usuário") }],
+
+    "Conhecido Nível I": [{ goal: 10, description: "Ser seguido por 10 usuários", repeatable: false, action: get_action("Ser Seguido") }],
+    "Conhecido Nível II": [{ goal: 50, description: "Ser seguido por 50 usuários", repeatable: false, action: get_action("Ser Seguido") }],
+    "Conhecido Nível III": [{ goal: 200, description: "Ser seguido por 200 usuários", repeatable: false, action: get_action("Ser Seguido") }],
+
+    "Divulgador Nível I": [{ goal: 1, description: "Compartilhar 1 recurso", repeatable: false, action: get_action("Compartilhar") }],
+    "Divulgador Nível II": [{ goal: 20, description: "Compartilhar 20 recursos", repeatable: false, action: get_action("Compartilhar") }],
+    "Divulgador Nível III": [{ goal: 100, description: "Compartilhar 100 recursos", repeatable: false, action: get_action("Compartilhar") }]
+  }
+
+  subject_requirements = {}
+  Subject.all.each do |subject|
+    subject_requirements["Proficiência em #{subject.name} Nível I"] = [{ goal: 20, description: "Utilizar 20 recursos de #{subject.name}", repeatable: false, action: get_action("Visualizar um Recurso de #{subject.name}") }]
+    subject_requirements["Proficiência em #{subject.name} Nível II"] = [{ goal: 50, description: "Utilizar 50 recursos de #{subject.name}", repeatable: false, action: get_action("Visualizar um Recurso de #{subject.name}") }]
+    subject_requirements["Proficiência em #{subject.name} Nível III"] = [{ goal: 150, description: "Utilizar 150 recursos de #{subject.name}", repeatable: false, action: get_action("Visualizar um Recurso de #{subject.name}") }]
+    subject_requirements["Proficiência em #{subject.name} Nível IV"] = [{ goal: 500, description: "Utilizar 500 recursos de #{subject.name}", repeatable: false, action: get_action("Visualizar um Recurso de #{subject.name}") }]
+    subject_requirements["Proficiência em #{subject.name} Nível V"] = [{ goal: 1000, description: "Utilizar 1000 recursos de #{subject.name}", repeatable: false, action: get_action("Visualizar um Recurso de #{subject.name}") }]
+  end
+  main_requirements.merge(subject_requirements)
+end
+
+requirements.each do |achievement, requirement|
+  requirement.each do |req|
+    get_achievement(achievement).requirements.where(req).first_or_create
+  end
+end
diff --git a/lib/tasks/gamefication.rake b/lib/tasks/gamefication.rake
new file mode 100644
index 0000000000000000000000000000000000000000..21498b36e82ae76fd008407c55c67f812503bd81
--- /dev/null
+++ b/lib/tasks/gamefication.rake
@@ -0,0 +1,155 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+namespace :gamefication do
+
+  desc 'Reset repeatable requirements/achievements [weekly, monthly or yearly]'
+  task :reset_repeatables, [:type] => [:environment] do |task, args|
+    if ["weekly", "monthly", "yearly"].include? args[:type]
+      repeatable_ids = Achievement.joins(:requirements).where(repeatable: args[:type], requirements: {repeatable: true}).pluck(:requirement_id)
+
+      start = 1
+      finish = 1000
+      batch_size = 1000
+      last_id = User.count > 0 ? User.last.id : 0
+      loop do
+        p "Resetting weekly Requirements for User ids: #{start} to #{finish}"
+        begin
+          User.find_each(start: start, finish: finish) do |user|
+            user.progresses.where(requirement_id: repeatable_ids).each do |progress|
+              progress.update(counter: 0)
+            end
+          end
+        rescue Exception => e
+          p 'Database error, going to sleep'
+          p e
+          p e.class
+          p e.message
+          ActiveRecord::Base.clear_active_connections!
+          # Sleeps for a while to wait database's recovery
+          sleep(60.seconds)
+        else
+          start += batch_size
+          finish += batch_size
+          break if start >= last_id
+        end
+      end
+    else
+      p "Repeatable type '#{args[:type]}' not supported."
+    end
+  end
+
+  desc 'Daily reset for resettable requirements/achievements'
+  task daily_reset: :environment do
+    resettable_ids = Achievement.joins(:requirements).where(resettable: true).pluck(:requirement_id)
+    frequent_req_ids = Achievement.joins(:requirements).where("name LIKE 'Frequente%'").pluck(:requirement_id)
+    repeatable_ids = Achievement.joins(:requirements).where(repeatable: "daily", requirements: {repeatable: true}).pluck(:requirement_id)
+
+    start = 1
+    finish = 1000
+    batch_size = 1000
+    last_id = User.count > 0 ? User.last.id : 0
+    loop do
+      p "Resetting daily Requirements for User ids: #{start} to #{finish}"
+      begin
+        User.find_each(start: start, finish: finish) do |user|
+          user.progresses.where(requirement_id: resettable_ids).each do |progress|
+            days = (Time.now - progress.updated_at)/1.day
+            frequent = frequent_req_ids.include? progress.requirement_id
+            if (days > 1 && !frequent) || (days >= 2 && frequent)
+              progress.update(counter: 0)
+            end
+          end
+          user.progresses.where(requirement_id: repeatable_ids).each do |progress|
+              progress.update(counter: 0)
+          end
+        end
+      rescue Exception => e
+        p 'Database error, going to sleep'
+        p e
+        p e.class
+        p e.message
+        ActiveRecord::Base.clear_active_connections!
+        # Sleeps for a while to wait database's recovery
+        sleep(60.seconds)
+      else
+        start += batch_size
+        finish += batch_size
+        break if start >= last_id
+      end
+    end
+  end
+
+  desc 'Update all users actions and give experience and achievements accordingly'
+  task complete_actions: :environment do
+    start = 1
+    finish = 1000
+    batch_size = 1000
+    last_id = User.count > 0 ? User.last.id : 0
+    loop do
+      p "Updating actions for User ids: #{start} to #{finish}"
+      begin
+        User.find_each(start: start, finish: finish) do |user|
+          complete_action("Publicar", user, user.learning_objects.published.count)
+          complete_action("Favoritar", user, user.likes.count)
+          complete_action("Avaliar", user, user.reviews.count)
+          complete_action("Comentar", user, user.reviews.where.not(description: nil).count)
+          complete_action("Seguir Usuário", user, user.following('User').count)
+          complete_action("Ser Seguido", user, user.followers.count)
+          complete_action("Fazer Download de um Recurso", user, user.downloads.count)
+
+          complete_action("Meses de Conta", user, (Time.now - user.created_at)/1.month)
+          complete_action("Visualizar um Recurso", user, user.views.where(viewable_type: 'User').count)
+          complete_action("Visualizar uma Coleção", user, user.views.where(viewable_type: 'Collection').count)
+          complete_action("Criar Coleção", user, user.collections.count)
+          complete_action("Adicionar Recurso a Coleção", user, user.collections.map { |i| i.collection_items.where(collectionable_type: "LearningObject").count }.sum)
+          complete_action("Adicionar Foto de Perfil", user) if !user.avatar.blank?
+          complete_action("Adicionar Capa de Perfil", user)  if !user.cover.blank?
+          complete_action("Adicionar Descrição do Usuário", user)  if !user.description.blank?
+          complete_action("Autenticação de Professor", user) if user.submitter_request == "accepted"
+        end
+      rescue Exception => e
+        p 'Database error, going to sleep'
+        p e
+        p e.class
+        p e.message
+        ActiveRecord::Base.clear_active_connections!
+        # Sleeps for a while to wait database's recovery
+        sleep(60.seconds)
+      else
+        start += batch_size
+        finish += batch_size
+        break if start >= last_id
+      end
+    end
+  end
+
+  private
+
+  def complete_action(action_name, user, quantity=1)
+    action = Action.find_by_name(action_name)
+    action_counter = user.action_counters.where(action: action).first
+    if action_counter.nil?
+      user.complete_action(action, quantity)
+    else
+      amount = quantity - action_counter.counter
+      user.complete_action(action, amount) if amount > 0
+    end
+  end
+end
diff --git a/lib/tasks/user.rake b/lib/tasks/user.rake
index 5cf6adbe9f51f3c7187add1310cbf2dbc5710d03..3c2999e6bbcb151f1375914ee0f6410300b0dd33 100644
--- a/lib/tasks/user.rake
+++ b/lib/tasks/user.rake
@@ -7,4 +7,11 @@ namespace :user do
       user.save!
     end
   end
+  desc "Add gamification relations to users created before gamification was implemented"
+  task gamify_existing_users: :environment do
+    for user in User.all
+      user.generate_gamification_relations
+    end
+  end
+
 end
diff --git a/spec/acceptance/achievements_spec.rb b/spec/acceptance/achievements_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..35558e6e3d72911cc28e28c452091a6aeab70236
--- /dev/null
+++ b/spec/acceptance/achievements_spec.rb
@@ -0,0 +1,132 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+require 'acceptance_helpers'
+require 'shared/contexts'
+
+resource 'Achievement' do
+  header 'Accept', 'application/json'
+  header 'Content-Type', 'application/json'
+
+  explanation "Users can perform some pre-defined actions in the system to unlock achievements and win rewards."
+
+  before { 12.times { create(:achievement) } }
+
+  let(:achievements) { Achievement.all }
+
+  get '/v1/achievements' do
+    parameter :limit, 'Limit of achievements'
+    parameter :offset, 'Offset of achievements'
+
+    let(:limit) { 12 }
+    let(:offset) { 0 }
+
+    example_request 'Get a list of achievements' do
+      expect(JSON.parse(response_body).map { |o| o['id'] }.sort).to eq(Achievement.limit(limit).offset(offset).pluck(:id).sort)
+      expect(status).to eq(200)
+    end
+  end
+
+  get '/v1/achievements/:id' do
+    let(:id) { achievements.first.id }
+
+    example 'Get an achievement' do
+      do_request
+      expect(path).to eq("/v1/achievements/#{id}") # `:id` is replaced with the value of `id`
+      expect(response_body).to eq(Helper.serialize(Achievement.find(id)))
+      expect(status).to eq(200)
+    end
+  end
+
+  post '/v1/achievements' do
+    include_context "authenticate_user_editor"
+
+    parameter :name, 'The name of the achievement', scope: :achievement
+    parameter :description, 'The description of the achievement', scope: :achievement
+    parameter :reward_experience, 'Amount of experience rewarded when the achievement is completed', scope: :achievement
+    parameter :reward_points, 'Amount of points rewarded when the achievement is completed', scope: :achievement
+    parameter :state, 'State of the achievement [inactive, active, deleted]', scope: :achievement
+    parameter :repeatable, 'Repeatability of the achievement [never, daily, weekly, monthly, yearly]', scope: :achievement
+    parameter :resettable, 'Whether the achievement can be reseted or not', scope: :achievement
+    parameter :requirements, 'Array with requirements ids', scope: :achievement
+
+    let(:name) { Faker::Name.name }
+    let(:description) { Faker::Lorem.paragraph}
+    let(:reward_experience) { rand(0..100) }
+    let(:reward_points) { rand(0..100) }
+    let(:state) { "active" }
+    let(:repeatable) { "daily" }
+    let(:resettable) { true }
+    let(:requirements) { [@requirement.id] }
+    let(:raw_post) { params.to_json }
+
+    before do
+      @requirement = create(:requirement)
+    end
+
+    example 'Creating an achievement' do
+      do_request
+      expect(status).to eq(201)
+    end
+  end
+
+  put '/v1/achievements/:id' do
+    include_context "authenticate_user_editor"
+
+    parameter :name, 'The name of the achievement', scope: :achievement
+    parameter :description, 'The description of the achievement', scope: :achievement
+    parameter :reward_experience, 'Amount of experience rewarded when the achievement is completed', scope: :achievement
+    parameter :reward_points, 'Amount of points rewarded when the achievement is completed', scope: :achievement
+    parameter :state, 'State of the achievement [inactive, active, deleted]', scope: :achievement
+    parameter :repeatable, 'Repeatability of the achievement [never, daily, weekly, monthly, yearly]', scope: :achievement
+    parameter :resettable, 'Whether the achievement can be reseted or not', scope: :achievement
+    parameter :requirements, 'Array with requirements ids', scope: :achievement
+
+    let(:id) { @achievement.id }
+    let(:description) { Faker::Lorem.paragraph }
+    let(:raw_post) { params.to_json }
+
+    before do
+      @achievement = create(:achievement)
+    end
+
+    example 'Updating an achievement' do
+      do_request
+      expect(status).to eq(200)
+    end
+  end
+
+  delete '/v1/achievements/:id' do
+    include_context "authenticate_user_editor"
+
+    let(:id) { @achievement.id }
+
+    before do
+      @achievement = create(:achievement)
+    end
+
+    example 'Destroying an achievement' do
+      do_request
+      expect(JSON.parse(response_body)['state']).to eq("deleted")
+      expect(status).to eq(200)
+    end
+
+  end
+
+end
diff --git a/spec/acceptance/action_counters_spec.rb b/spec/acceptance/action_counters_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4ec2f06da156435bac65a39114819fe49e79f2d6
--- /dev/null
+++ b/spec/acceptance/action_counters_spec.rb
@@ -0,0 +1,57 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+require 'acceptance_helpers'
+
+resource 'Action_Counters' do
+    header 'Accept', 'application/json'
+    header 'Content-Type', 'application/json'
+
+    explanation "A counter for pre-actions in the system done from Users"
+
+    before { 12.times { create(:action_counter) } }
+
+    let(:action_counter) { ActionCounter.all }
+
+    get '/v1/action_counters' do
+
+        parameter :limit, 'Limit of actions'
+        parameter :offset, 'Offset of actions'
+
+        let(:limit) { 12 }
+        let(:offset) { 0 }
+
+        example_request 'Get a list of action counters' do
+                expect( JSON.parse(response_body).map { |o| o['id'] }.sort ).to eq( ActionCounter.limit(limit).offset(offset).pluck(:id).sort ) 
+            
+            expect(status).to eq(200)
+        end
+    end
+
+    get '/v1/action_counters/:id' do
+        let(:id) { action_counter.first.id }
+
+        example 'Get an action counter' do
+            do_request
+            expect(path).to eq("/v1/action_counters/#{id}")
+            expect(response_body).to eq(Helper.serialize(ActionCounter.find(id)))
+            expect(status).to eq(200)
+        end
+    end
+end
\ No newline at end of file
diff --git a/spec/acceptance/actions_spec.rb b/spec/acceptance/actions_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cc132310b7fbccd71427d579e0f8d55078c6ba7d
--- /dev/null
+++ b/spec/acceptance/actions_spec.rb
@@ -0,0 +1,112 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+require 'acceptance_helpers'
+require 'shared/contexts'
+
+resource 'Action' do
+  header 'Accept', 'application/json'
+  header 'Content-Type', 'application/json'
+
+  explanation "Users can perform some pre-defined Actions in the system to collect experience."
+
+  before { 12.times { create(:action) } }
+
+  let(:actions) { Action.all }
+
+  get '/v1/actions' do
+    parameter :limit, 'Limit of actions'
+    parameter :offset, 'Offset of actions'
+
+    let(:limit) { 12 }
+    let(:offset) { 0 }
+
+    example_request 'Get a list of actions' do
+      expect(JSON.parse(response_body).map { |o| o['id'] }.sort).to eq(Action.limit(limit).offset(offset).pluck(:id).sort)
+      expect(status).to eq(200)
+    end
+  end
+
+  get '/v1/actions/:id' do
+    let(:id) { actions.first.id }
+
+    example 'Get an action' do
+      do_request
+      expect(path).to eq("/v1/actions/#{id}") # `:id` is replaced with the value of `id`
+      expect(response_body).to eq(Helper.serialize(Action.find(id)))
+      expect(status).to eq(200)
+    end
+  end
+
+  post '/v1/actions' do
+    include_context "authenticate_user_editor"
+
+    parameter :name, 'The name of the action', scope: :action_params
+    parameter :description, 'The description of the action', scope: :action_params
+    parameter :reward_experience, 'Amount of experience rewarded when the action is completed', scope: :action_params
+
+    let(:name) { Faker::Name.name }
+    let(:description) { Faker::Lorem.paragraph}
+    let(:reward_experience) { rand(0..100) }
+    let(:raw_post) { params.to_json }
+
+    example 'Creating an action' do
+      do_request
+      expect(status).to eq(201)
+    end
+  end
+
+  put '/v1/actions/:id' do
+    include_context "authenticate_user_editor"
+
+    parameter :name, 'The name of the action', scope: :action_params
+    parameter :description, 'The description of the action', scope: :action_params
+    parameter :reward_experience, 'Amount of experience rewarded when the action is completed', scope: :action_params
+
+    let(:id) { @action.id }
+    let(:description) { Faker::Lorem.paragraph }
+    let(:raw_post) { params.to_json }
+
+    before do
+      @action = create(:action)
+    end
+
+    example 'Updating an action' do
+      do_request
+      expect(status).to eq(200)
+    end
+  end
+
+  delete '/v1/actions/:id' do
+    include_context "authenticate_user_editor"
+
+    let(:id) { @action.id }
+
+    before do
+      @action = create(:action)
+    end
+
+    example 'Destroying an action' do
+      do_request
+      expect(status).to eq(200)
+    end
+
+  end
+
+end
diff --git a/spec/acceptance/items_spec.rb b/spec/acceptance/items_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9721889b8b706ccde89c54b397d627ae8be29384
--- /dev/null
+++ b/spec/acceptance/items_spec.rb
@@ -0,0 +1,140 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+require 'acceptance_helpers'
+require 'shared/contexts'
+
+resource 'Item' do
+  header 'Accept', 'application/json'
+  header 'Content-Type', 'application/json'
+
+  explanation "Users can unlock items through achievements and points won in the platform."
+
+  before { 12.times { create(:item) }
+    2.times { create(:achievement) } 
+  }
+
+  let(:items) { Item.all }
+  let(:achievements) { Achievement.all }
+
+  get '/v1/items' do
+    parameter :limit, 'Limit of items'
+    parameter :offset, 'Offset of items'
+    parameter :item_type, 'Type of the items'
+    parameter :op, 'Operation to filter the items by price [lt, gt, eq]'
+    parameter :price, 'Price of the items'
+    parameter :unlock_rule, 'Rule to unlock the items [achievement, purchase]'
+
+    let(:limit) { 12 }
+    let(:offset) { 0 }
+
+    example_request 'Get a list of items' do
+      expect(JSON.parse(response_body).map { |o| o['id'] }.sort).to eq(items.limit(limit).offset(offset).pluck(:id).sort)
+      expect(status).to eq(200)
+    end
+  end
+
+  get '/v1/items/:id' do
+    let(:id) { items.first.id }
+
+    example 'Get an item' do
+      do_request
+      expect(path).to eq("/v1/items/#{id}") # `:id` is replaced with the value of `id`
+      expect(response_body).to eq(Helper.serialize(items.find(id)))
+      expect(status).to eq(200)
+    end
+  end
+
+  post '/v1/items' do
+    include_context "authenticate_user_editor"
+
+    parameter :name, 'The name of the item', scope: :item
+    parameter :description, 'The description of the item', scope: :item
+    parameter :price, 'Price of the item in points', scope: :item
+    parameter :discount, 'Amount of discount given to the item price', scope: :item
+    parameter :state, 'State of the item [inactive, active, removed]', scope: :item
+    parameter :item_type, 'Type of the item [avatar_frame, badge, card_frame, cover_frame]', scope: :item
+    parameter :achievement_id, 'The id of the achievement needed to unlock the item', scope: :item
+    parameter :image, 'The image of the item', scope: :item
+
+    let(:name) { Faker::Name.name }
+    let(:description) { Faker::Lorem.paragraph}
+    let(:price) { rand(0..100) }
+    let(:discount) { rand(0..100) }
+    let(:state) { "active" }
+    let(:item_type) { "badge" }
+    let(:achievement_id) { achievements.first.id }
+    let(:image) { @image }
+    let(:raw_post) { params.to_json }
+
+    before do
+      @image = "data:image/png;base64,"
+      @image += Base64.encode64(fixture_file_upload(file_fixture('img_test.png'), binary: true).tempfile.open.read.force_encoding(Encoding::UTF_8)).strip
+    end
+
+    example 'Creating an item' do
+      do_request
+      expect(status).to eq(201)
+    end
+  end
+
+  put '/v1/items/:id' do
+    include_context "authenticate_user_editor"
+
+    parameter :name, 'The name of the item', scope: :item
+    parameter :description, 'The description of the item', scope: :item
+    parameter :price, 'Price of the item in points', scope: :item
+    parameter :discount, 'Amount of discount given to the item price', scope: :item
+    parameter :state, 'State of the item [inactive, active, removed]', scope: :item
+    parameter :item_type, 'Type of the item [avatar_frame, badge, card_frame, cover_frame]', scope: :item
+    parameter :achievement_id, 'The id of the achievement needed to unlock the item', scope: :item
+    parameter :image, 'The image of the item', scope: :item
+
+    let(:id) { @item.id }
+    let(:description) { Faker::Lorem.paragraph }
+    let(:raw_post) { params.to_json }
+
+    before do
+      @item = create(:item)
+    end
+
+    example 'Updating an item' do
+      do_request
+      expect(status).to eq(200)
+    end
+  end
+
+  delete '/v1/items/:id' do
+    include_context "authenticate_user_editor"
+
+    let(:id) { @item.id }
+
+    before do
+      @item = create(:item)
+    end
+
+    example 'Destroying an item' do
+      do_request
+      expect(JSON.parse(response_body)['state']).to eq("removed")
+      expect(status).to eq(200)
+    end
+
+  end
+
+end
diff --git a/spec/acceptance/progresses_spec.rb b/spec/acceptance/progresses_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5d6af853e8bf1ce10242c193b56a93126df01892
--- /dev/null
+++ b/spec/acceptance/progresses_spec.rb
@@ -0,0 +1,62 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+require 'acceptance_helpers'
+require 'shared/contexts'
+
+resource 'Progress' do
+    header 'Accept', 'application/json'
+    header 'Content-Type', 'application/json'
+
+    explanation " The progress for achievements in the system for each user"
+
+    before { 12.times { create(:progress) } }
+
+    let(:progresses) { Progress.all }
+
+   
+    get '/v1/progresses' do   #index
+        include_context "authenticate_user"
+
+        before { 12.times { create(:progress, user: @user) }}
+
+        parameter :limit, 'Limit of progresses'
+        parameter :offset, 'Offset of progresses'
+
+        let(:limit) { 12 }
+        let(:offset) { 0 }
+
+        example_request 'Get a list of progresses from a user' do
+            expect( JSON.parse(response_body).map { |o| o['id'] }.sort ).to eq( @user.progresses.limit(limit).offset(offset).pluck(:id).sort )
+            expect(status).to eq(200)
+        end
+    end
+
+    get '/v1/progresses/:id' do
+        
+        let(:id) { progresses.first.id }      
+
+        example 'Get a specific progress' do
+            do_request
+            expect(path).to eq("/v1/progresses/#{id}")
+            expect(response_body).to eq(Helper.serialize(Progress.find(id)))
+            expect(status).to eq(200)
+        end
+    end
+end
\ No newline at end of file
diff --git a/spec/acceptance/requirements_spec.rb b/spec/acceptance/requirements_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..903e8092bf39bb7e0ce1351c93b98d807c83b329
--- /dev/null
+++ b/spec/acceptance/requirements_spec.rb
@@ -0,0 +1,123 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+require 'acceptance_helpers'
+require 'shared/contexts'
+
+resource 'Requirement' do
+  header 'Accept', 'application/json'
+  header 'Content-Type', 'application/json'
+
+  explanation "Achievements have requirements that users must complete to unlock them."
+
+  before { 12.times { create(:requirement) }
+    2.times { create(:achievement) } 
+    1.times { create(:action) } 
+  }
+
+  let(:requirements) { Requirement.all }
+  let(:all_achievements) { Achievement.all }
+  let(:action) { Action.first }
+
+  get '/v1/requirements' do
+    parameter :limit, 'Limit of requirements'
+    parameter :offset, 'Offset of requirements'
+
+    let(:limit) { 12 }
+    let(:offset) { 0 }
+
+    example_request 'Get a list of requirements' do
+      expect(JSON.parse(response_body).map { |o| o['id'] }.sort).to eq(requirements.limit(limit).offset(offset).pluck(:id).sort)
+      expect(status).to eq(200)
+    end
+  end
+
+  get '/v1/requirements/:id' do
+    let(:id) { requirements.first.id }
+
+    example 'Get a requirement' do
+      do_request
+      expect(path).to eq("/v1/requirements/#{id}") # `:id` is replaced with the value of `id`
+      expect(response_body).to eq(Helper.serialize(requirements.find(id)))
+      expect(status).to eq(200)
+    end
+  end
+
+  post '/v1/requirements' do
+    include_context "authenticate_user_editor"
+
+    parameter :description, 'The description of the requirement', scope: :requirement
+    parameter :goal, 'Amount of actions neccessary to fulfill the requirement', scope: :requirement
+    parameter :repeatable, 'Whether the achievement can be repeated or not', scope: :requirement
+    parameter :action_id, 'The id of the action needed for the requirement', scope: :requirement
+    parameter :achievements, 'Array with achievements ids', scope: :requirement
+
+    let(:description) { Faker::Lorem.paragraph}
+    let(:goal) { rand(0..100) }
+    let(:repeatable) { true }
+    let(:action_id) { action.id }
+    let(:achievements) { [all_achievements.first.id] }
+    let(:raw_post) { params.to_json }
+
+    example 'Creating an requirement' do
+      do_request
+      expect(status).to eq(201)
+    end
+  end
+
+  put '/v1/requirements/:id' do
+    include_context "authenticate_user_editor"
+
+    parameter :description, 'The description of the requirement', scope: :requirement
+    parameter :goal, 'Amount of actions neccessary to fulfill the requirement', scope: :requirement
+    parameter :repeatable, 'Whether the achievement can be repeated or not', scope: :requirement
+    parameter :action_id, 'The id of the action needed for the requirement', scope: :requirement
+    parameter :achievements, 'Array with achievements ids', scope: :requirement
+
+    let(:id) { @requirement.id }
+    let(:description) { Faker::Lorem.paragraph }
+    let(:raw_post) { params.to_json }
+
+    before do
+      @requirement = create(:requirement)
+    end
+
+    example 'Updating a requirement' do
+      do_request
+      expect(status).to eq(200)
+    end
+  end
+
+  delete '/v1/requirements/:id' do
+    include_context "authenticate_user_editor"
+
+    let(:id) { @requirement.id }
+
+    before do
+      @requirement = create(:requirement)
+    end
+
+    example 'Destroying a requirement' do
+      do_request
+      expect(status).to eq(200)
+    end
+
+  end
+
+end
diff --git a/spec/acceptance/unlocked_achievements_spec.rb b/spec/acceptance/unlocked_achievements_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d9c1067726c194d1aed197659c3ceb639d422f0b
--- /dev/null
+++ b/spec/acceptance/unlocked_achievements_spec.rb
@@ -0,0 +1,61 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.require 'acceptance_helpers'
+
+require 'acceptance_helpers'
+require 'shared/contexts'
+
+resource 'Unlocked Achievements' do
+  header 'Accept', 'application/json'
+  header 'Content-Type', 'application/json'
+
+  explanation "Unlocked Achievements from a User"
+
+  before { 12.times { create(:unlocked_achievement) } }
+
+  let(:unlocked_achievements) { UnlockedAchievement.all }
+
+  get '/v1/unlocked_achievements' do   #index
+    include_context "authenticate_user"
+
+    before { 12.times { create(:unlocked_achievement, user: @user) }}
+
+    parameter :limit, 'Limit of unlocked achievements'
+    parameter :offset, 'Offset of unlocked achievements'
+
+    let(:limit) { 12 }
+    let(:offset) { 0 }
+
+    example_request 'Get a list of unlocked achievements from a user' do
+      expect( JSON.parse(response_body).map { |o| o['id'] }.sort ).to eq( @user.unlocked_achievements.limit(limit).offset(offset).pluck(:id).sort )
+      expect(status).to eq(200)
+    end
+  end
+
+  get '/v1/unlocked_achievements/:id' do
+
+    let(:id) { unlocked_achievements.first.id }      
+
+    example 'Get a specific unlocked achievement' do
+      do_request
+      expect(path).to eq("/v1/unlocked_achievements/#{id}")
+      expect(response_body).to eq(Helper.serialize(UnlockedAchievement.find(id)))
+      expect(status).to eq(200)
+    end
+  end
+end
\ No newline at end of file
diff --git a/spec/acceptance/user_items_spec.rb b/spec/acceptance/user_items_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..81d0f2e43c7d16400b5d4d6a84644c41bab548e5
--- /dev/null
+++ b/spec/acceptance/user_items_spec.rb
@@ -0,0 +1,65 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.require 'acceptance_helpers'
+
+require 'acceptance_helpers'
+require 'shared/contexts'
+
+resource 'User Items' do
+  header 'Accept', 'application/json'
+  header 'Content-Type', 'application/json'
+
+  explanation "Items owned by an User"
+
+  before { 1.times { create(:user_item) } }
+
+  let(:user_items) { UserItem.all }
+
+  get '/v1/user_items' do   #index
+    include_context "authenticate_user"
+
+    before { 12.times { create(:user_item, user: @user) }}
+
+    parameter :limit, 'Limit of user items'
+    parameter :offset, 'Offset of user items'
+    parameter :item_type, 'Item_type of the user items'
+    parameter :op, 'Operation to filter the user items by price [lt, gt, eq]'
+    parameter :price, 'Price of the user items'
+    parameter :unlock_rule, 'Rule to unlock the user items [achievement, purchase]'
+
+    let(:limit) { 12 }
+    let(:offset) { 0 }
+
+    example_request 'Get a list of user items from a user' do
+      expect( JSON.parse(response_body).map { |o| o['id'] }.sort ).to eq(@user.items.active.limit(limit).offset(offset).pluck(:id).sort)
+      expect(status).to eq(200)
+    end
+  end
+
+  get '/v1/user_items/:id' do
+      
+    let(:id) { user_items.first.id }      
+
+    example 'Get a specific user item' do
+      do_request
+      expect(path).to eq("/v1/user_items/#{id}")
+      expect(response_body).to eq(Helper.serialize(UserItem.find(id)))
+      expect(status).to eq(200)
+    end
+  end
+end
\ No newline at end of file
diff --git a/spec/acceptance/users_spec.rb b/spec/acceptance/users_spec.rb
index 1073fa2bd7f34889e0d0d57d9b9d464ba84b20d6..09f73b8de9ac181d7143d4c72b06d1e0200c4214 100644
--- a/spec/acceptance/users_spec.rb
+++ b/spec/acceptance/users_spec.rb
@@ -420,4 +420,167 @@ resource 'Users' do
       expect(status).to eq(200)
     end
   end
+
+  post '/v1/users/complete_action' do
+    include_context "authenticate_user"
+
+    parameter :action_id, 'The id of the action the current user is completing'
+    parameter :quantity, 'The amount of actions the current user is completing (default = 1)'
+
+    let(:action_id) { @action.id }
+    let(:quantity) { @quantity }
+    let(:raw_post) { params.to_json }
+
+    before do
+      @action = create(:action)
+      @quantity = 1
+    end
+
+    example 'Complete an action' do
+      do_request
+      expect(JSON.parse(response_body)['action']['id']).to eq(@action.id)
+      expect(JSON.parse(response_body)['counter']).to eq(@quantity)
+      expect(status).to eq(200)
+    end
+  end
+
+  post '/v1/users/purchase_item' do
+    include_context "authenticate_user"
+
+    parameter :item_id, 'The id of the item the current user is purchasing'
+
+    let(:item_id) { @item.id }
+    let(:raw_post) { params.to_json }
+
+    before do
+      @item = create(:item)
+    end
+
+    example 'Purchase an item' do
+      do_request
+      expect(JSON.parse(response_body).last['item']['id']).to eq(@item.id)
+      expect(status).to eq(200)
+    end
+  end
+
+  post '/v1/users/equip_item' do
+    include_context "authenticate_user"
+
+    parameter :item_id, 'The id of the item the current user is equiping'
+
+    let(:item_id) { @item.id }
+    let(:raw_post) { params.to_json }
+
+    before do
+      @item = create(:item)
+    end
+
+    example 'Equip an item' do
+      do_request
+      expect(JSON.parse(response_body).select {|i| i['being_used'] }.last['item']['id']).to eq(@item.id)
+      expect(status).to eq(200)
+    end
+  end
+
+  post '/v1/users/unequip_item' do
+    include_context "authenticate_user"
+
+    parameter :item_id, 'The id of the item the current user is unequiping'
+
+    let(:item_id) { @item.id }
+    let(:raw_post) { params.to_json }
+
+    before do
+      @item = create(:item)
+      @user.equip_item(@item)
+    end
+
+    example 'Unequip an item' do
+      do_request
+      expect(JSON.parse(response_body).select {|i| !i['being_used'] }.last['item']['id']).to eq(@item.id)
+      expect(status).to eq(200)
+    end
+  end
+
+  post '/v1/users/remove_item' do
+    include_context "authenticate_user"
+
+    parameter :item_id, 'The id of the item the current user is removing'
+
+    let(:item_id) { @item.id }
+    let(:raw_post) { params.to_json }
+
+    before do
+      @item = create(:item)
+      @user_item = create(:user_item, user: @user, item: @item)
+    end
+
+    example 'Remove an item from the user inventory' do
+      do_request
+      expect(JSON.parse(response_body).select {|i| i['item']['id'] == @item.id }).to eq([])
+      expect(status).to eq(200)
+    end
+  end
+
+  get '/v1/users/action_counters' do
+    include_context "authenticate_user"
+    
+    before { 12.times { create(:action_counter, user: @user) }}
+
+    parameter :limit, 'Limit of action counters'
+    parameter :offset, 'Offset of action counters'
+
+    let(:limit) { 12 }
+    let(:offset) { 0 }
+
+    example_request 'Get a list of the current user\'s action counters' do
+      # active model serializer may render model associations in different order for collections (array of items), so we're verifing only returned ids
+      expect(JSON.parse(response_body).map { |o| o['id'] }.sort).to eq(@user.action_counters.limit(limit).offset(offset).pluck(:id).sort)
+      expect(status).to eq(200)
+    end
+  end
+
+  get '/v1/users/completed_achievements' do
+    include_context "authenticate_user"
+
+    before { 12.times { create(:unlocked_achievement, user: @user) }}
+    
+    parameter :limit, 'Limit of completed achievements'
+    parameter :offset, 'Offset of completed achievements'
+
+    let(:limit) { 12 }
+    let(:offset) { 0 }
+
+    example_request 'Get a list of the current user\'s completed achievements' do
+      # active model serializer may render model associations in different order for collections (array of items), so we're verifing only returned ids
+      expect(JSON.parse(response_body).map { |o| o['id'] }.sort).to eq(@user.unlocked_achievements.limit(limit).offset(offset).pluck(:id).sort)
+      expect(status).to eq(200)
+    end
+  end
+
+  get '/v1/users/:id/items' do
+    
+    parameter :id, 'The id of the user who owns the items'
+    parameter :being_used, "Filter the user's items by whether they're being used or not. [true, false, all]"
+    parameter :item_type, "Filter the user's items by their type. [avatar_frame, badge, card_frame, cover_frame, all]"
+    parameter :limit, 'Limit of user items'
+    parameter :offset, 'Offset of user items'
+
+    let(:id) {@user.id}
+    let(:limit) { 12 }
+    let(:offset) { 0 }
+    let(:being_used) { "all" }
+    let(:item_type) { "all" }
+
+    before do
+      @user = create(:user)
+      6.times { create(:user_item, user: @user, being_used: true) }
+      6.times { create(:user_item, user: @user, being_used: false) }
+    end
+
+    example_request 'Get a list of the user\'s items' do
+      expect(JSON.parse(response_body).map { |o| o['id'] }.sort).to eq(@user.user_items.limit(limit).offset(offset).pluck(:id).sort)
+      expect(status).to eq(200)
+    end
+  end
 end
diff --git a/spec/factories/achievements.rb b/spec/factories/achievements.rb
new file mode 100644
index 0000000000000000000000000000000000000000..340771936edc99c35691791e80f2c396d29b151b
--- /dev/null
+++ b/spec/factories/achievements.rb
@@ -0,0 +1,31 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+
+FactoryBot.define do
+  factory :achievement do
+    sequence(:name) { |i| "Achievement #{i}" }
+    description { Faker::Lorem.paragraph}
+    reward_experience { rand(1..100) }
+    reward_points { rand(1..100) }
+    state { 1 }
+    repeatable { 1 }
+    resettable { true }
+  end
+end
diff --git a/spec/factories/action_counters.rb b/spec/factories/action_counters.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b43b5426afd3b7788cc9c051e6e3b723b91829d3
--- /dev/null
+++ b/spec/factories/action_counters.rb
@@ -0,0 +1,25 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+FactoryBot.define do
+    factory :action_counter do
+        user
+        action
+    end
+end
\ No newline at end of file
diff --git a/spec/factories/actions.rb b/spec/factories/actions.rb
new file mode 100644
index 0000000000000000000000000000000000000000..340e4d01c5d35779a7d53db771a58cebb9d9f945
--- /dev/null
+++ b/spec/factories/actions.rb
@@ -0,0 +1,27 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+
+FactoryBot.define do
+  factory :action do
+    sequence(:name) { |i| "Action #{i}" }
+    description { Faker::Lorem.paragraph }
+    reward_experience { rand(100..300) }
+  end
+end
diff --git a/spec/factories/experience_level_maps.rb b/spec/factories/experience_level_maps.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9b6ac68966572b61ce3e7dd3b694a2744d357cbf
--- /dev/null
+++ b/spec/factories/experience_level_maps.rb
@@ -0,0 +1,5 @@
+FactoryBot.define do
+  factory :experience_level_map do
+    
+  end
+end
diff --git a/spec/factories/items.rb b/spec/factories/items.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2943818ac24906ec57be07672360f541a123d7fa
--- /dev/null
+++ b/spec/factories/items.rb
@@ -0,0 +1,30 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+FactoryBot.define do
+  factory :item do
+    sequence(:name) { |i| "Item #{i}" }
+    description { Faker::Lorem.paragraph}
+    price { rand(0..100) }
+    discount { rand(0..100) }
+    state { 1 }
+    item_type { 1 }
+    image { Rack::Test::UploadedFile.new('spec/fixtures/files/img_test.png', 'image/png') }
+  end
+end
diff --git a/spec/factories/progresses.rb b/spec/factories/progresses.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8f219c473da06ee3ffcf29addcb96828aab5e454
--- /dev/null
+++ b/spec/factories/progresses.rb
@@ -0,0 +1,25 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+FactoryBot.define do
+    factory :progress do |p|
+        user
+        requirement
+        counter { 0 }
+    end
+end
\ No newline at end of file
diff --git a/spec/factories/requirements.rb b/spec/factories/requirements.rb
new file mode 100644
index 0000000000000000000000000000000000000000..daded7273f412a950f8ec61454866cb00e362424
--- /dev/null
+++ b/spec/factories/requirements.rb
@@ -0,0 +1,27 @@
+
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+FactoryBot.define do
+  factory :requirement do
+    action
+    goal { rand(0..10) }
+    description { Faker::Lorem.paragraph }
+    repeatable { false }
+  end
+end
\ No newline at end of file
diff --git a/spec/factories/unlocked_achievements.rb b/spec/factories/unlocked_achievements.rb
new file mode 100644
index 0000000000000000000000000000000000000000..229dee257aa383287daf516f7cb202575b2aebb4
--- /dev/null
+++ b/spec/factories/unlocked_achievements.rb
@@ -0,0 +1,24 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+FactoryBot.define do
+    factory :unlocked_achievement do |p|
+        achievement
+        user
+    end
+end
\ No newline at end of file
diff --git a/spec/factories/user_items.rb b/spec/factories/user_items.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e5027dcb329046500429b79bf87341e4bf31d866
--- /dev/null
+++ b/spec/factories/user_items.rb
@@ -0,0 +1,24 @@
+# Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
+# Departamento de Informatica - Universidade Federal do Parana
+#
+# This file is part of portalmec.
+#
+# portalmec is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# portalmec is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with portalmec.  If not, see <http://www.gnu.org/licenses/>.
+
+FactoryBot.define do
+    factory :user_item do |p|
+        item
+        user
+    end
+end
\ No newline at end of file
diff --git a/spec/fixtures/files/img_test.png b/spec/fixtures/files/img_test.png
new file mode 100644
index 0000000000000000000000000000000000000000..0881b10fef4760663252c7e4e8d8980912818350
Binary files /dev/null and b/spec/fixtures/files/img_test.png differ
diff --git a/spec/models/experience_level_map_spec.rb b/spec/models/experience_level_map_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..85722f8c1998be9fe23d1e2d0017b0262a42eb74
--- /dev/null
+++ b/spec/models/experience_level_map_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe ExperienceLevelMap, type: :model do
+  pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/shared/contexts.rb b/spec/shared/contexts.rb
index 8478e0e30d971f3008eb0d8c246a7217cb153fb3..3304617f06ba501b87b8fa83d3dde824fe4e9d86 100644
--- a/spec/shared/contexts.rb
+++ b/spec/shared/contexts.rb
@@ -29,7 +29,7 @@ RSpec.shared_context "authenticate_user", shared_context: :metadata do
   let(:role) { Role.all }
 
   before do
-    @user = create(:user, roles: [role.find_by(name: 'submitter')])
+    @user = create(:user, roles: [role.find_by(name: 'submitter')], points: 100)
     @auth_headers = @user.create_new_auth_token
   end