Class: TimeCop::Accountability

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

Constant Summary collapse

DAYS_PER_WEEK =
5
HOURS_PER_WEEK =
32
HOURS_PER_DAY =
HOURS_PER_WEEK / DAYS_PER_WEEK
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) ⇒ Accountability

Returns a new instance of Accountability.



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

def initialize(username:, password:, subdomain: 'wildland', date: Date.today, report_builder: nil, email: nil)
  @client = Harvest.client(username: username, password: password, subdomain: subdomain)
  @date = date
  @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

#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



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

def 
  client.
end

#business_days_between(start_date, end_date) ⇒ Object



83
84
85
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
# File 'lib/time_cop/accountability.rb', line 83

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



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

def current_hours_delta
  total_quarter_time_tracked - expected_quarter_hours_to_today
end

#date_from_period(period) ⇒ Object



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

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

#default_userObject



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

def default_user
  .who_am_i
end

#end_of_quarter_dateObject



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

def end_of_quarter_date
  date_from_period(get_quarter[1])
end

#expected_quarter_hoursObject



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

def expected_quarter_hours
  total_week_days.to_f * HOURS_PER_DAY
end

#expected_quarter_hours_to_todayObject



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

def expected_quarter_hours_to_today
  business_days_between(start_of_quarter_date, date) * HOURS_PER_DAY
end

#fetch_user(email) ⇒ Object



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

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

#get_quarterObject



59
60
61
# File 'lib/time_cop/accountability.rb', line 59

def get_quarter
  select_quarter.first[1]
end


152
153
154
155
156
157
158
159
# File 'lib/time_cop/accountability.rb', line 152

def print_report
  puts "Quarter Period: #{start_of_quarter_date} #{end_of_quarter_date}"
  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



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

def quarterly_hours_delta
  expected_quarter_hours - total_quarter_time_tracked
end

#quarterly_hours_per_business_day_neededObject



144
145
146
147
148
149
150
# File 'lib/time_cop/accountability.rb', line 144

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



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

def quarterly_tracked_time_by_user
  @quarterly_tracked_time_by_user ||= report_builder.generate
end

#select_quarterObject



47
48
49
50
51
52
53
54
55
56
57
# File 'lib/time_cop/accountability.rb', line 47

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



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

def start_of_quarter_date
  date_from_period(get_quarter[0])
end

#total_quarter_daysObject



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

def total_quarter_days
  (end_of_quarter_date - start_of_quarter_date).to_i
end

#total_quarter_time_trackedObject



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

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

#total_week_daysObject



124
125
126
# File 'lib/time_cop/accountability.rb', line 124

def total_week_days
  business_days_between(start_of_quarter_date, end_of_quarter_date)
end