How to restart current_level for every third missed_day (3 hits you got out!)?

enter image description here

For example, if the user is at level 4: Day 50 , it goes back to Day 45 .

  case n_days     
      when 0..9     # Would go back to 0
        1
      when 10..24   # Back to 10
        2
      when 25..44   # Back to 25
        3
      when 45..69   # Back to 45
        4
      when 70..99   # Back to 70
        5
      else
        "Mastery"
    end

      

Then let's say it slips back into place, this time until Day 68 , if he checks 3 missed_days

again, it gets dropped again on Day 45 :

enter image description here

_form (presented above):

<label id="<%= @habit.id %>" class="habit-id"> Missed: </label>
  <% @habit.levels.each_with_index do |level, index| %>
    <p>
      <label id="<%= level.id %>" class="level-id"> Level <%= index + 1 %>: </label>
      <%= check_box_tag nil, true, level.missed_days > 0, {class: "habit-check"} %>
      <%= check_box_tag nil, true, level.missed_days > 1, {class: "habit-check"} %>
      <%= check_box_tag nil, true, level.missed_days > 2, {class: "habit-check"} %>
    </p>
  <% end %>

      

habit.rb

class Habit < ActiveRecord::Base
    belongs_to :user
    has_many :comments, as: :commentable
    has_many :levels
    serialize :committed, Array
    validates :date_started, presence: true
    before_save :current_level
    acts_as_taggable
    scope :private_submit, -> { where(private_submit: true) }
    scope :public_submit, -> { where(private_submit: false) }

attr_accessor :missed_one, :missed_two, :missed_three

    def save_with_current_level
        self.levels.build
        self.levels.build
        self.levels.build
        self.levels.build
        self.levels.build
        self.save
    end

    def self.committed_for_today
      today_name = Date::DAYNAMES[Date.today.wday].downcase
      ids = all.select { |h| h.committed.include? today_name }.map(&:id)
      where(id: ids)
    end 

    def current_level_strike
      levels[current_level - 1] # remember arrays indexes start at 0
    end

    def current_level
          return 0 unless date_started
          def committed_wdays
            committed.map do |day|    
              Date::DAYNAMES.index(day.titleize)
            end
          end

          def n_days
            ((date_started.to_date)..Date.today).count do |date| 
              committed_wdays.include? date.wday
            end - self.missed_days
          end     

      case n_days     
          when 0..9
            1
          when 10..24
            2
          when 25..44
            3
          when 45..69
            4
          when 70..99
            5
          else
            6
        end
    end
end

      

habit.js

$(document).ready(function() {
  var handleChange = function() {
    habit = $(this).parent().prev().attr("id");
    level = $('label', $(this).parent()).attr("id");
    if ($(this).is(":checked")) {
      $.ajax({
        url: "/habits/" + habit + "/levels/" + level + "/days_missed",
        method: "POST"
      });
    } else {
      $.ajax({
        url: "/habits/" + habit + "/levels/" + level + "/days_missed/1",
        method: "DELETE"
      });
    }
    if (!$('input[type="checkbox"]:not(:checked)', $(this).parent()).length) {
      /* this is just an example, you will have to ammend this */
      $(this).parent().append($('<input type="checkbox" class="habit-check">'));
      $(this).parent().append($('<input type="checkbox" class="habit-check">'));
      $(this).parent().append($('<input type="checkbox" class="habit-check">'));
      $(".habit-check").on('change',handleChange);
    }
  }
  $(".habit-check").on('change',handleChange);
});

      

days_missed_controller.rb

class DaysMissedController < ApplicationController
  before_action :logged_in_user, only: [:create, :destroy]

  def create
    habit = Habit.find(params[:habit_id])
    habit.missed_days = habit.missed_days + 1
    habit.save!
    level = habit.levels.find(params[:level_id])
    level.missed_days = level.missed_days + 1
    level.save!
    head :ok # this returns an empty response with a 200 success status code
  end

  def destroy
    habit = Habit.find(params[:habit_id])
    habit.missed_days = habit.missed_days - 1
    habit.save
    level = habit.levels.find(params[:level_id])
    level.missed_days = level.missed_days - 1
    level.save!
    head :ok # this returns an empty response with a 200 success status code
  end
end

      

Here's the Gist : https://gist.github.com/RallyWithGalli/c66dee6dfb9ab5d338c2

Please let me know if you need more code, explanation or pictures :)

+3


source to share


2 answers


Here is my solution:

You need to keep track of the days you lost at the moment you got 3 missed days (you need to add a field to the level):

migration file .rb

class AddDaysLostToLevels < ActiveRecord::Migration
  def change
    add_column :levels, :days_lost, :integer,   default: 0
  end

      

end



Then change the days_missed controller to reset when you get 3 missed days and keep the days you lost when you start the level again (in the days_lost variable):

class DaysMissedController < ApplicationController

  def create
    habit = Habit.find(params[:habit_id])
    habit.missed_days = habit.missed_days + 1
    habit.save!
    level = habit.levels.find(params[:level_id])
    level.missed_days = level.missed_days + 1
    if level.missed_days == 3
      level.missed_days = 0
      level.days_lost += habit.calculate_days_lost + 1
    end
    ...remain the same

      

Now we need to define the methods "calculate_days_lost" and "real_missed_days" in the habit.rb file.

Remember to use self.real_missed_days instead of self.missed_days in the current_level: method (they differ from each other because you can start the level again). The new habit.rb will look like this:

class Habit < ActiveRecord::Base
belongs_to :user
has_many :comments, as: :commentable
has_many :levels
serialize :committed, Array
validates :date_started, presence: true
before_save :current_level
acts_as_taggable
scope :private_submit, -> { where(private_submit: true) }
scope :public_submit, -> { where(private_submit: false) }

attr_accessor :missed_one, :missed_two, :missed_three

def save_with_current_level
    self.levels.build
    self.levels.build
    self.levels.build
    self.levels.build
    self.levels.build
    self.save
end

def self.committed_for_today
  today_name = Date::DAYNAMES[Date.today.wday].downcase
  ids = all.select { |h| h.committed.include? today_name }.map(&:id)
  where(id: ids)
end 

def current_level_strike
  levels[current_level - 1] # remember arrays indexes start at 0
end

  def real_missed_days
     value = 0
     levels.each do |level|
       if level.missed_days != nil 
         value += level.missed_days + level.days_lost
       end  
     end
     value
  end

  def committed_wdays
   committed.map do |day|    
    Date::DAYNAMES.index(day.titleize)
   end
  end

  def n_days
    ((date_started.to_date)..Date.today).count do |date| 
      committed_wdays.include? date.wday
    end - self.real_missed_days
  end    

  def calculate_days_lost
    case n_days   
      when 0..9
        n_days
      when 10..24
        n_days-10
      when 25..44
        n_days-25
      when 45..69
        n_days-45
      when 70..99
        n_days-70
      else
        n_days-100
    end
  end

 def current_level
   return 0 unless date_started
   case n_days    
      when 0..9
        1
      when 10..24
        2
      when 25..44
        3
      when 45..69
        4
      when 70..99
        5
      else
        6
    end
  end

end

      

+2


source


If I understand correctly what you are trying to achieve here, we expect the user to do something every day for several days. If he doesn't do it 3 times in a given "level period", you want to reset his "progress level", right?

If this is the case, the easiest solution would be to add another variable, for example: days_lost

.

Then you can apply it in habit.rb :

n_days = ((date_started.to_date)..Date.today).count { |date| committed_wdays.include? date.wday } - self.missed_days - self.days_lost

      

Remember to update it every time the user misses 3 times, which looks like days_missed_controller.rb :

def create
  ...
  if missed_days == 3
    missed_days = 0
    days_lost = <days from beginning of the level>
  end
  ...
end

      

UPDATE



<days from beginning of the level>

could be counted by creating another helper variable eg. pending_days

and give it an initial value 0

. Every day pending_days += 1

.

Next we deal:

1: The player "skips" 3 days at his current level - we add days from the start of the level to negative (lost) days and start counting again

days_lost += pending_days
pending_days = 0

      

2: The player "misses" for less than 3 days - we just increase his level and start counting again, because he is at a new level

pending_days = 0

      

+1


source







All Articles