Class: TimeCop::Accountability

Inherits:
Object
  • Object
show all
Defined in:
lib/time_cop/accountability.rb

Constant Summary collapse

DAYS_PER_WEEK =
5.0
QUARTERLY_PERIODS =
{
  q1: [{month: 1, day: 1}, {month: 3, day: 31}],
  q2: [{month: 4, day: 1}, {month: 6, day: 30}],
  q3: [{month: 7, day: 1}, {month: 9, day: 30}],
  q4: [{month: 10, day: 1}, {month: 12, day: 31}]
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(username:, password:, subdomain: 'wildland', date: Date.today, report_builder: nil, email: nil, hours_per_week: 32) ⇒ Accountability

Returns a new instance of Accountability.



17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/time_cop/accountability.rb', line 17

def initialize(username:, password:, subdomain: 'wildland', date: Date.today, report_builder: nil, email: nil, hours_per_week: 32)
  @client = Harvest.client(username: username, password: password, subdomain: subdomain)
  @date = date
  @hours_per_week = hours_per_week
  @report_builder = report_builder ||
    ReportBuilder.new(
      client: client,
      user: email ? fetch_user(email) : default_user,
      start_date: start_of_quarter_date,
      end_date: end_of_quarter_date
    )
end

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



6
7
8
# File 'lib/time_cop/accountability.rb', line 6

def client
  @client
end

#dateObject (readonly)

Returns the value of attribute date.



6
7
8
# File 'lib/time_cop/accountability.rb', line 6

def date
  @date
end

#hours_per_weekObject (readonly)

Returns the value of attribute hours_per_week.



6
7
8
# File 'lib/time_cop/accountability.rb', line 6

def hours_per_week
  @hours_per_week
end

#report_builderObject (readonly)

Returns the value of attribute report_builder.



6
7
8
# File 'lib/time_cop/accountability.rb', line 6

def report_builder
  @report_builder
end

Instance Method Details

#accountObject



34
35
36
# File 'lib/time_cop/accountability.rb', line 34

def 
  client.
end

#business_days_between(start_date, end_date) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/time_cop/accountability.rb', line 86

def business_days_between(start_date, end_date)
  days_between = (end_date - start_date).to_i
  return 0 unless days_between > 0

  # Assuming we need to calculate days from 9th to 25th, 10-23 are covered
  # by whole weeks, and 24-25 are extra days.
  #
  # Su Mo Tu We Th Fr Sa    # Su Mo Tu We Th Fr Sa
  #        1  2  3  4  5    #        1  2  3  4  5
  #  6  7  8  9 10 11 12    #  6  7  8  9 ww ww ww
  # 13 14 15 16 17 18 19    # ww ww ww ww ww ww ww
  # 20 21 22 23 24 25 26    # ww ww ww ww ed ed 26
  # 27 28 29 30 31          # 27 28 29 30 31
  whole_weeks, extra_days = days_between.divmod(7)

  unless extra_days.zero?
    # Extra days start from the week day next to start_day,
    # and end on end_date's week date. The position of the
    # start date in a week can be either before (the left calendar)
    # or after (the right one) the end date.
    #
    # Su Mo Tu We Th Fr Sa    # Su Mo Tu We Th Fr Sa
    #        1  2  3  4  5    #        1  2  3  4  5
    #  6  7  8  9 10 11 12    #  6  7  8  9 10 11 12
    # ## ## ## ## 17 18 19    # 13 14 15 16 ## ## ##
    # 20 21 22 23 24 25 26    # ## 21 22 23 24 25 26
    # 27 28 29 30 31          # 27 28 29 30 31
    #
    # If some of the extra_days fall on a weekend, they need to be subtracted.
    # In the first case only corner days can be days off,
    # and in the second case there are indeed two such days.
    extra_days -= if start_date.next_day.wday <= end_date.wday
                    [start_date.next_day.sunday?, end_date.saturday?].count(true)
                  else
                    2
                  end
  end

  (whole_weeks * 5) + extra_days
end

#current_hours_deltaObject



139
140
141
# File 'lib/time_cop/accountability.rb', line 139

def current_hours_delta
  total_quarter_time_tracked - expected_quarter_hours_to_today
end

#date_from_period(period) ⇒ Object



46
47
48
# File 'lib/time_cop/accountability.rb', line 46

def date_from_period(period)
    Date.new(date.year, period[:month], period[:day])
end

#default_userObject



42
43
44
# File 'lib/time_cop/accountability.rb', line 42

def default_user
  .who_am_i
end

#end_of_quarter_dateObject



70
71
72
# File 'lib/time_cop/accountability.rb', line 70

def end_of_quarter_date
  date_from_period(get_quarter[1])
end

#expected_quarter_hoursObject



131
132
133
# File 'lib/time_cop/accountability.rb', line 131

def expected_quarter_hours
  total_week_days.to_f * hours_per_day
end

#expected_quarter_hours_to_todayObject



135
136
137
# File 'lib/time_cop/accountability.rb', line 135

def expected_quarter_hours_to_today
  business_days_between(start_of_quarter_date, date) * hours_per_day
end

#fetch_user(email) ⇒ Object



38
39
40
# File 'lib/time_cop/accountability.rb', line 38

def fetch_user(email)
  client.users.find(email)
end

#get_quarterObject



62
63
64
# File 'lib/time_cop/accountability.rb', line 62

def get_quarter
  select_quarter.first[1]
end

#hours_per_dayObject



30
31
32
# File 'lib/time_cop/accountability.rb', line 30

def hours_per_day
  @hours_per_week / DAYS_PER_WEEK
end


155
156
157
158
159
160
161
162
163
# File 'lib/time_cop/accountability.rb', line 155

def print_report
  puts "Quarter Period: #{start_of_quarter_date} #{end_of_quarter_date}"
  puts "Hours Per Week: #{@hours_per_week}"
  puts "Current Surplus(+)/Deficit(-): #{current_hours_delta.round(2)}"
  puts "Quarterly Hour Target: #{expected_quarter_hours.round(2)}"
  puts "Current Quarterly Charged Hours: #{total_quarter_time_tracked.round(2)}"
  puts "Total Hours Needed By End Of Quarter: #{quarterly_hours_delta.round(2)}"
  puts "Hours Per Business Day Average Needed To Reach Goal: #{quarterly_hours_per_business_day_needed.round(2)}"
end

#quarterly_hours_deltaObject



143
144
145
# File 'lib/time_cop/accountability.rb', line 143

def quarterly_hours_delta
  expected_quarter_hours - total_quarter_time_tracked
end

#quarterly_hours_per_business_day_neededObject



147
148
149
150
151
152
153
# File 'lib/time_cop/accountability.rb', line 147

def quarterly_hours_per_business_day_needed
  if business_days_between(date, end_of_quarter_date) == 0
    quarterly_hours_delta
  else
    quarterly_hours_delta / business_days_between(date, end_of_quarter_date).to_f
  end
end

#quarterly_tracked_time_by_userObject



74
75
76
# File 'lib/time_cop/accountability.rb', line 74

def quarterly_tracked_time_by_user
  @quarterly_tracked_time_by_user ||= report_builder.generate
end

#select_quarterObject



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/time_cop/accountability.rb', line 50

def select_quarter
  QUARTERLY_PERIODS.select do |quarter, range|
    start_of_range = range[0]
    end_of_range = range[1]

    start_date_of_range = date_from_period(start_of_range)
    end_date_of_range = date_from_period(end_of_range)

    (date >= start_date_of_range) && (date <= end_date_of_range)
  end
end

#start_of_quarter_dateObject



66
67
68
# File 'lib/time_cop/accountability.rb', line 66

def start_of_quarter_date
  date_from_period(get_quarter[0])
end

#total_quarter_daysObject



82
83
84
# File 'lib/time_cop/accountability.rb', line 82

def total_quarter_days
  (end_of_quarter_date - start_of_quarter_date).to_i
end

#total_quarter_time_trackedObject



78
79
80
# File 'lib/time_cop/accountability.rb', line 78

def total_quarter_time_tracked
  quarterly_tracked_time_by_user.map { |t| t.hours }.reduce(:+)
end

#total_week_daysObject



127
128
129
# File 'lib/time_cop/accountability.rb', line 127

def total_week_days
  business_days_between(start_of_quarter_date, end_of_quarter_date)
end