Skip to content

Instantly share code, notes, and snippets.

@stabenfeldt
Created August 6, 2010 09:01
Show Gist options
  • Save stabenfeldt/511065 to your computer and use it in GitHub Desktop.
Save stabenfeldt/511065 to your computer and use it in GitHub Desktop.
class Assignment < ActiveRecord::Base
# $LOUD_N_CLEAR = true
# Counts workdays between two Dates (not Time)
def count_weekdays(startdate,stopdate)
@workdays = 0
startdate.to_date.upto(stopdate.to_date) do |date|
if date.wday.to_s =~ /[0,1,2,3,4]/
@workdays +=1
end
end
@workdays
end
# Count hours for the first day, it may not be a full day.
def hours_the_first_day(date)
logger.debug "The first day is #{date}" if $LOUD_N_CLEAR
# If the start- or stop-time are missing time, add it.
if self.from.hour == 0
self_from_hour = 8
else
self_from_hour = self.from.hour
end
if self.to.hour == 0
self_to_hour = 8
else
self_to_hour = self.to.hour
end
yr, mon, day, hr, min = ParseDate.parsedate date.to_s
firstday_start = Time.utc(yr,mon,day,self_from_hour,self.from.min).to_f # Epoch start of day
firstday_stop = Time.utc(yr,mon,day,WORKDAY_STOP).to_f # Same day, at hour #{WORKDAY_STOP}
logger.debug "firstday_start = #{firstday_start} - firstday_stop = #{firstday_stop} (#{yr},#{mon},#{day},#{WORKDAY_STOP})" if $LOUD_N_CLEAR
unless date.weekend?
@hours_total += (firstday_stop-firstday_start)/3600
@hours_total -= 0.5 # LUNCH
end
logger.debug "hours_the_first_day = #{@hours_total} hours" if $LOUD_N_CLEAR
logger.debug "(#{firstday_stop}-#{firstday_start})/3600 - (firstday_stop-firstday_start)/3600" if $LOUD_N_CLEAR
end
# Used to count hours for the entire assignment
def hours_prognosed
@hours_total = 0
@assignment_start_day = self.from.to_date.to_s.gsub!('-','').to_i # => 20010101
@assignment_stop_day = self.to.to_date.to_s.gsub!('-','').to_i # => 20010102
# Is the first day also the last day?
if self.to.to_date == self.from.to_date
last_day(self.to.to_datetime)
else
hours_the_first_day(self.from)
# And go on with the rest until we reach end of assignment
next_day = self.from
while next_day = next_day.tomorrow
if next_day.to_date == self.to.to_date
last_day(self.to.to_datetime)
break
else
unless next_day.weekend?
@hours_total += WORKDAY_LASTS_FOR_HOURS
end
end
end
end
@hours_total
end
# It´s the last day of the assignment, which may be shorter.
def last_day(last_date)
unless last_date.class.to_s =~ /DateTime/
last_date = last_date.to_datetime
end
# raise "Input must be DateTime, got #{last_date.class}" unless last_date.class.to_s =~ /(DateTime)/
# If the start- or stop-time are missing time, add it.
if self.from.hour == 0
self_from_hour = 8
else
self_from_hour = self.from.hour
end
if self.to.hour == 0
self_to_hour = 8
else
self_to_hour = self.to.hour
end
if last_date.weekend?
return # Unless self.count_weekends_too
else
yr,mon,day,hr,min = ParseDate.parsedate last_date.to_s
# raise "last_date: #{last_date}"
logger.debug "args: yr,mon,day,hr,min => #{yr},#{mon},#{day},#{hr},#{min}" if $LOUD_N_CLEAR
lastday_start = Time.utc(yr,mon,day,WORKDAY_START).to_f # Last day, at 08:00 in Epoch
lastday_stop = Time.utc(yr,mon,day,self_to_hour,self.to.min).to_f # Epoch at end of day
if $LOUD_N_CLEAR
logger.debug "last_day() => WORKDAY_START: #{WORKDAY_START}"
logger.debug "last_day() => LASTDAY_START: #{lastday_start} (#{yr},#{mon},#{day},#{WORKDAY_START}) - LASTDAY_STOP = #{lastday_stop} (#{yr},#{mon},#{day},#{hr},#{min})"
logger.debug "WOHA! Hold your horses boy! Self.to is #{self.to} last_day is #{last_date.to_s}. Which gives us endpoint at #{Time.at(lastday_stop)}"
end
@hours_total += (lastday_stop-lastday_start)/3600
@hours_total -= 0.5 # LUNCH
end
logger.debug "exiting with #{@hours_total} hours_total" if $LOUD_N_CLEAR
end
def hours_written_last_week
last_monday = (Date.today - 7) # Todays´s monday
friday = last_monday + 5
# Returns timestamp|hours for given project_id and user_id for the last week
query = AchievoProject.connection.execute("
SELECT h.activitydate, sum(time/60) as hours from hours h left
JOIN phase ph on h.phaseid=ph.id left
JOIN project p on ph.projectid=p.id
WHERE h.userid = #{self.person.get_achievo_id} AND p.id = #{self.project_id}
AND h.activitydate > date_add(curdate(), INTERVAL -1 month)
group by h.activitydate;
")
return query.fetch_hash["hours"].to_f
end
def abort_if_assignment_wasnt_last_week
# If the assignment ended more than one week ago stop, do nothing. It´s no longer last week.
gap = last_monday.to_f - self.to.to_f
if gap > (86400*7)
logger.debug 'The assignment ended more than a week ago, <<last week>> is way gone.' if $LOUD_N_CLEAR
end
end
def hours_prognosed_last_week
@hours_total = 0
if self.to.to_f > self.from.to_f
logger.debug "Assignment must have a valid start and end date: #{self.from} => #{self.to}"
return
end
return if abort_if_assignment_wasnt_last_week
# If the start- or stop-time are missing time, add it.
if self.from.hour == 0
self_from_hour = 8
else
self_from_hour = self.from.hour
end
if self.to.hour == 0
self_to_hour = 8
else
self_to_hour = self.to.hour
end
# Calculations has to start from a monday, find previous.
logger.debug "Calculations has to start from a monday, find previous." if $LOUD_N_CLEAR
calculation_day = last_monday
# Find last monday, 7 days earlier than calculation_day.
last_monday = one_week_before(calculation_day)
logger.debug "monday 7 days earlier than calculation_day (#{calculation_day.to_date} wday:#{calculation_day.wday}) is #{last_monday.to_date}(#{last_monday.wday})" if $LOUD_N_CLEAR
# From calculation_day (The first monday after the week were´re working with)
# Find end of the week in question.
# Find friday 16 o´clock
end_of_week = find_end_of_week(calculation_day)
#logger.debug "END_OF_WEEK: #{end_of_week}" if $LOUD_N_CLEAR
logger.debug "Looking for friday, end of day. Moving from #{calculation_day.to_date} (#{calculation_day.wday}) towards the end of week (friday) we pass.." if $LOUD_N_CLEAR
logger.debug "three days before calculation_day #{calculation_day.to_date} (#{calculation_day.wday}) we got end_of_week #{end_of_week.to_date} (#{end_of_week.wday})" if $LOUD_N_CLEAR
# If the project began after monday, count from there.
if self.from.to_f >= last_monday.to_f
start_of_week = self.from
else
start_of_week = last_monday
end
# Does it last till end_of_week?
# raise "end_of_week is class :#{end_of_week}"
if self.to.to_f < end_of_week.to_time.to_f
check_to = self.to.to_date
logger.debug "check_to is #{check_to.wday}" if $LOUD_N_CLEAR
logger.debug "it lasts till end_of_week - self.to.to_f (#{self.to.to_f}) < end_of_week.to_f (#{end_of_week.to_time.to_f})" if $LOUD_N_CLEAR
else
check_to = end_of_week.to_date
logger.debug "check_to is #{check_to.wday}"
logger.debug "it DOES NOT lasts till end_of_week - self.to.to_f [#{self.to}] (#{self.to.to_f}) < end_of_week.to_f (#{end_of_week.to_time.to_f}) [#{end_of_week.to_time}]" if $LOUD_N_CLEAR
end
# Is the first day also the last day?
if self.to.to_date == self.from.to_date
last_day(self.to.to_datetime)
else
hours_the_first_day(start_of_week)
if $LOUD_N_CLEAR
logger.debug "The first day brought #{@hours_total} hours"
logger.debug "Started at #{self.from} -> 16 o clock"
logger.debug "working towards end of friday"
end
# And go on with the rest until end of friday.
next_day = start_of_week
logger.debug "first day after start-monday: #{next_day} (#{next_day.wday}) - should be thuesday (2): #{next_day.tomorrow} (#{next_day.tomorrow.wday})" if $LOUD_N_CLEAR
while next_day = next_day.tomorrow
if next_day.to_date.to_s == check_to.to_s
logger.debug "hours before last_day: #{@hours_total}" if $LOUD_N_CLEAR
last_day(next_day)
logger.debug "break, counting last_day. Hours this far: #{@hours_total}" if $LOUD_N_CLEAR
break
else
unless next_day.weekend?
@hours_total += WORKDAY_LASTS_FOR_HOURS
end
logger.debug "hours_prognosed_last_week(): hours_total: #{@hours_total}" if $LOUD_N_CLEAR
end
end
end
@hours_total
end
def hours_in_matrix_on_date(assignment, date)
as = Assignment.find(assignment)
if date > as.from and date < as.to # date is in the period of time the assignment spans across.
7.5 * as.prognos_weight_percentage
end
end
# Given a date-range, number of workdays are returned
# TODO: Could be extended to subtract holidays too.
def workdays
count_weekdays(self.from.to_date,self.to.to_date)
end
# Returns assignments that were active the previous week for all users.
def people_with_active_assignments
start_of_previous_week = one_week_before(last_monday)
end_of_previous_week = find_end_of_week(start_of_previous_week)
@people = [ ]
Assignment.all(:conditions => ['assignments.to > ?', end_of_previous_week]).each do |p|
@people << p
end
end
# Hours total written in Achievo for project.
def hours_written_in_achievo
unless self.project_id && self.person_id
logger.debug "Assignment is missing project_id or person_id"
return
end
query = AchievoProject.connection.execute("
SELECT p.id, p.name, sum(time)/60 AS allhours,
SUM(case when h.activitydate > date_add(curdate(), INTERVAL -1 week)
THEN time/60 else 0 end) AS lastweek FROM hours h left
JOIN phase ph ON h.phaseid=ph.id left
JOIN project p ON ph.projectid=p.id
WHERE h.userid = #{self.person.get_achievo_id} AND p.id = #{self.project_id}
group by p.id;")
return query.fetch_hash["allhours"].to_f
end
def diff_with_achievo
logger.debug "ASSIGNMENT_ID: #{self.id}" if $LOUD_N_CLEAR
if self.hours_prognosed.to_f > self.hours_written_in_achievo["lastweek"].to_f
self.hours_written_in_achievo["lastweek"].to_f - self.hours_prognosed rescue nil
else
self.hours_written_in_achievo["lastweek"].to_f - self.hours_prognosed rescue nil
end
end
def prognos_weight_percentage
self.prognos_weight.to_f/100.to_f
end
def include?(date)
from.to_date <= date && to.to_date >= date
end
def full_day?(date)
not half_day?(date)
end
def half_day?(date)
return true if to.to_date == date && to.hour == 12
return true if from.to_date == date && from.hour == 13
return false
end
def first_half?(date)
return half_day?(date) && to.to_date == date && to.hour == 12
end
def second_half?(date)
return half_day?(date) && from.to_date == date && from.hour == 13
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment