class RulingTimerHistory include Mongoid::Document include Mongoid::Timestamps field :work_time_str, type: String, default: "00:00:00" field :rest_time_str, type: String, default: "00:00:00" field :all_work_times_seconds, type: Integer, default: 0 field :all_rest_times_seconds, type: Integer, default: 0 field :work_times, type: Array, default: [] field :rest_times, type: Array, default: [] field :sub_task_ids, type: Array, default: [] #store RulingTimerSubTask id field :tasks, type: Array, default: [] #store name only field :tasks_finished, type: Array, default: [] #store index only field :summary, type: String, default: "" field :time_offset, type: String, default: "+8" field :events, type: Array, default: [] field :date, type: String, default: "" belongs_to :user def fix_work_times(store=true) time_now = DateTime.now.utc work_times_range = self.work_times.each_slice(2).to_a.map{|a,b| convert_datetime(a)..(b.nil? ? time_now : convert_datetime(b))} work_times_range = eliminate_intersection(work_times_range) new_work_times_range = merge_ranges(work_times_range.uniq).flat_map{|range| [range.first,range.last]} new_work_times_range = new_work_times_range[0...-1] if new_work_times_range.last == time_now self.work_times = new_work_times_range self.calc("work_times",false,true) self.work_time_str = transform_second_to_time(self.all_work_times_seconds) self.save if store self.work_times end def merge_work_times(new_work_times,store=true) time_now = DateTime.now.utc work_times_range = self.work_times.each_slice(2).to_a.map{|a,b| convert_datetime(a)..(b.nil? ? time_now : convert_datetime(b))} new_work_times_range = new_work_times.each_slice(2).to_a.map{|a,b| convert_datetime(a)..(b.nil? ? time_now : convert_datetime(b))} work_times_range = eliminate_intersection(work_times_range) new_work_times_range = eliminate_intersection(new_work_times_range) new_work_times_range = work_times_range + new_work_times_range new_work_times_range = eliminate_intersection(new_work_times_range).sort_by{|range| range.first.to_i} new_work_times_range = merge_ranges(new_work_times_range.uniq).flat_map{|range| [range.first,range.last]} new_work_times_range = new_work_times_range[0...-1] if new_work_times_range.last == time_now self.work_times = new_work_times_range self.calc("work_times",false,true) self.work_time_str = transform_second_to_time(self.all_work_times_seconds) self.save if store self.work_times end def merge_ranges(ranges) (0...(ranges.count)).each do |i| next if ranges[i+1].nil? if ranges[i].last == ranges[i+1].first ranges[i+1] = (ranges[i].first .. ranges[i+1].last) ranges[i] = nil end end ranges.compact end def eliminate_intersection(times_range) times_range = times_range.sort_by{|range| range.first.to_i} times_range.each_with_index do |range,i| next if range.nil? intersects = times_range[i+1..-1].map{|r| range & r} if intersects.compact.count != 0 intersects.each_with_index do |intersect,j| next if intersect.nil? if range.last > intersect.last #overlap all times_range[i+1+j] = nil else if range.first == intersect.first times_range[i] = nil else times_range[i] = (range.first..intersect.first) end end end end end times_range.compact end def update_task_name(task_id,new_name) idx = self.sub_task_ids.index(task_id) self.tasks[idx] = new_name self.save end def remove_task(task_id) sub_task = RulingTimerSubTask.find(task_id) rescue nil idx = self.sub_task_ids.index(task_id) self.sub_task_ids.delete(task_id) self.tasks.delete_at(idx) self.tasks_finished = self.tasks_finished.to_a.map do |task_idx| if task_idx == idx nil elsif task_idx > idx task_idx - 1 else task_idx end end.compact self.save! sub_task.stop if sub_task end def get_work_times(display_seond=false) work_times = self.work_times.clone if display_seond time_format = "%H:%M:%S" else time_format = "%H:%M" end work_times.map!{|t| convert_datetime(t).new_offset(self.time_offset).strftime(time_format)} end def convert_datetime(time) if time.class == Time return time.to_datetime elsif time.class == DateTime return time elsif time.class == String return DateTime.parse(time) else return Time.at(time).to_datetime #time is seconds end end def get_infos return {"work" => self.work_time_str, "rest" => self.rest_time_str} end def calc(field_name,padding=true,recalc=false) time_infos = self.send(field_name).clone rescue [] record_field_name = "all_#{field_name}_seconds" tmp_seconds = self.send(record_field_name) all_seconds = 0 start_index = 0 if !recalc all_seconds = tmp_seconds start_index = time_infos.count - 1 start_index -= (start_index % 2) time_infos = time_infos[start_index..-1].to_a end time_infos.push(DateTime.now.utc) if (padding && time_infos.count % 2 == 1) time_infos.each_with_index do |t,i| if i % 2 == 0 next_t = time_infos[i+1] if !next_t.nil? all_seconds += ((convert_datetime(next_t) - convert_datetime(t)) * 1.day).round else break end end end self.send("#{record_field_name}=",all_seconds) return all_seconds end def recalc_all self.calc("work_times",false,true) self.calc("rest_times",false,true) self.work_time_str = transform_second_to_time(self.all_work_times_seconds) self.rest_time_str = transform_second_to_time(self.all_rest_times_seconds) self.save return self.to_json end def getPaddedComp(comp) return ((comp.to_i < 10) ? ('0' + comp.to_s) : comp.to_s) end def transform_second_to_time(seconds) seconds = 0 if seconds.nil? hour = 3600 minute = 60 total_hour = getPaddedComp(seconds / hour) rest_seconds = seconds % hour total_minute = getPaddedComp(rest_seconds / minute) total_second = getPaddedComp(rest_seconds % minute) return (total_hour + ":" + total_minute + ":" + total_second) end end