class RulingTimerTemp include Mongoid::Document include Mongoid::Timestamps field :status, type: String, default: "stop" # working, rest, stop 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 before_create do |record| time_now = DateTime.now.new_offset(0) record.date = time_now.new_offset(self.time_offset).strftime("%Y/%m/%d %w") if record.date.blank? end after_save do @work_times_exec = nil @rest_times_exec = nil end def get_attrs(except_fields=[]) attrs = {} self.fields.except(*(["_id","created_at","updated_at"] + except_fields)).each do |k, v| if (v.options[:localize] rescue false) attrs["#{k}_translations"] = self[k] else attrs[k] = self[k] end end attrs end def update_work_times(work_times,store=true) work_times = work_times.to_a time_now = DateTime.now.new_offset(0) self.date = time_now.new_offset(self.time_offset).strftime("%Y/%m/%d %w") if self.date.blank? date_str = self.date.split(" ")[0] work_times = work_times.to_a.map do |t| if t.blank? nil else DateTime.parse("#{date_str} #{t}#{self.time_offset}").utc end end self.work_times = work_times self.save if store self.work_times end def fix_work_times(store=true) time_now = DateTime.now.new_offset(0) 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.new_offset(0) 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 get_last_work_time last_work_time = self.work_times.last if last_work_time.nil? last_work_time = DateTime.now.new_offset(0).new_offset(self.time_offset) else last_work_time = convert_datetime(last_work_time).new_offset(self.time_offset) end return last_work_time end def get_first_work_time first_work_time = self.work_times.first if first_work_time.nil? first_work_time = DateTime.now.new_offset(0).new_offset(self.time_offset) else first_work_time = convert_datetime(first_work_time).new_offset(self.time_offset) end return first_work_time end def check_and_store unless self.new_record? time_now = DateTime.now.new_offset(0) date_str = time_now.new_offset(self.time_offset).strftime("%Y/%m/%d %w") if self.date != date_str self.stop self.store end end end def start(offset=nil) self.time_offset = offset if offset self.check_and_store self.status = "working" time_now = DateTime.now.new_offset(0) self.date = time_now.new_offset(self.time_offset).strftime("%Y/%m/%d %w") if self.work_times.count % 2 == 0 self.work_times.push(time_now) end if self.rest_times.count % 2 == 1 self.rest_times.push(time_now) end self.calc("work_times",false) self.calc("rest_times",false) 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 self.start_all_task return self.to_json end def reset_all(except_fields=[]) unless self.new_record? change_fields = self.changes.except(*(except_fields.map{|f| f.to_s})) change_fields.each do |k,v| self.send("#{k}=",v[0]) end end end def start_all_task temp_task_ids = self.sub_task_ids if self.tasks_finished.count != 0 temp_task_ids = temp_task_ids.select.with_index{|id,i| !(self.tasks_finished.include?(i))} end temp_tasks = RulingTimerSubTask.where(:id.in=>temp_task_ids).to_a temp_tasks.each{|task| task.start} end def stop_all_task temp_task_ids = self.sub_task_ids if self.tasks_finished.count != 0 temp_task_ids = temp_task_ids.select.with_index{|id,i| !(self.tasks_finished.include?(i))} end temp_tasks = RulingTimerSubTask.where(:id.in=>temp_task_ids).to_a temp_tasks.each{|task| task.stop} end def stop time_now = DateTime.now.new_offset(0) tmp_date = Date.parse(self.date.split(" ")[0]) time_now_date = time_now.new_offset(self.time_offset).to_date @new_ruling_timer = nil if tmp_date != time_now_date push_time = tmp_date.to_datetime.new_offset(self.time_offset).end_of_day.new_offset(0) need_create_history = true else push_time = time_now need_create_history = false end field_name = nil if self.work_times.count % 2 == 1 self.work_times.push(push_time) field_name = "work_times" end if self.rest_times.count % 2 == 1 self.rest_times.push(push_time) field_name = "rest_times" end if need_create_history && field_name tmp_attrs = self.get_attrs(["work_time_str", "rest_time_str", "all_work_times_seconds", "all_rest_times_seconds", "work_times", "rest_times", "status"]) tmp_attrs["all_#{field_name}_seconds"] = 86400 tmp_attrs["#{field_name.singularize}_str"] = "24:00:00" self.store self.send("#{field_name}=", [time_now.new_offset(self.time_offset).beginning_of_day.new_offset(0), time_now]) ((tmp_date + 1.day)...time_now_date).each do |d| tmp_date_str = d.strftime('%Y/%m/%d %w') begin_of_d = DateTime.parse(d.strftime("%Y/%m/%d 00:00:00#{self.time_offset}")) end_of_d = begin_of_d.end_of_day tmp_attrs[field_name] = [begin_of_d.new_offset(0), end_of_d.new_offset(0)] tmp_attrs["date"] = tmp_date_str RulingTimerHistory.create(tmp_attrs) end end self.status = "stop" self.calc("work_times",false) self.calc("rest_times",false) 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 self.stop_all_task return self.to_json end def rest self.status = "rest" time_now = DateTime.now.new_offset(0) if self.work_times.count % 2 == 1 self.work_times.push(time_now) end if self.rest_times.count % 2 == 0 self.rest_times.push(time_now) end self.calc("work_times",false) self.calc("rest_times",false) self.save self.stop_all_task return self.to_json 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 to_json self.calc("work_times") self.calc("rest_times") work_seconds = self.all_work_times_seconds rest_seconds = self.all_rest_times_seconds self.reset_all return {"work" => work_seconds, "rest" => rest_seconds} end def get_infos self.calc("work_times") self.calc("rest_times") 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.reset_all([:work_time_str,:rest_time_str]) return {"work" => self.work_time_str, "rest" => self.rest_time_str} 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 getPaddedComp(comp) return ((comp.to_i < 10) ? ('0' + comp.to_s) : comp.to_s) end def self.getPaddedComp(comp) return ((comp.to_i < 10) ? ('0' + comp.to_s) : comp.to_s) end def self.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 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 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 if self.send("#{field_name}_changed?") || (time_infos.count % 2 == 1) instance_variable_name = "@#{field_name}_exec" instance_variable = instance_variable_get(instance_variable_name) if instance_variable.nil? instance_variable_set(instance_variable_name, true) last_val = self.send("#{field_name}_was").to_a.last if last_val idx = time_infos.index(last_val) if idx start_index = idx + (idx % 2 == 0 ? 0 : 1) time_infos = time_infos[start_index..-1] end end else all_seconds = 0 #recalc end else return all_seconds end end time_infos.push(DateTime.now.new_offset(0)) if (padding && time_infos.count % 2 == 1) prev_t = nil 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 + ((prev_t && prev_t == t) ? 0.second : 1.second) else break end else prev_t = t end end self.send("#{record_field_name}=",all_seconds) return all_seconds end def add_event(startt,endt,event) self.events << {"start"=>startt,"end"=> endt,"event"=>event} self.save end def delete_event(idx) self.events.delete_at(idx) self.save end def store self.calc("work_times") self.calc("rest_times") 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) fields = self.fields.except("_id","created_at","updated_at","status").keys if self.all_work_times_seconds != 0 || self.all_rest_times_seconds != 0 ruling_timer_history = RulingTimerHistory.new fields.each do |f| field_name = f if (self.fields[f].options[:localize] rescue false) field_name = "#{f}_translations" end ruling_timer_history.send("#{field_name}=",self.send(field_name)) end ruling_timer_history.date = self.get_first_work_time.new_offset(self.time_offset).strftime("%Y/%m/%d %w") ruling_timer_history.user = self.user ruling_timer_history.save @timer_history = ruling_timer_history end available_locales = I18n.available_locales not_reset_fields = ["time_offset","user_id","tasks_finished","sub_task_ids","tasks"] fields.each do |f| next if not_reset_fields.include?(f) default_value = self.fields[f].options[:default] rescue nil if (self.fields[f].options[:localize] rescue false) field_name = "#{f}_translations" self.send("#{f}_translations=",available_locales.map{|l| [l.to_s,default_value]}.to_h) else self.send("#{f}=",default_value) end end if self.tasks_finished.count != 0 temp_task_ids = self.sub_task_ids.clone temp_tasks = self.tasks.clone temp_tasks_finished = self.tasks_finished.clone.sort.reverse temp_tasks_finished.each do |idx| temp_task_ids.delete_at(idx) temp_tasks.delete_at(idx) end self.sub_task_ids = temp_task_ids self.tasks = temp_tasks self.tasks_finished = [] end time_now = DateTime.now.new_offset(0) self.created_at = time_now self.updated_at = time_now self.date = time_now.new_offset(self.time_offset).strftime("%Y/%m/%d %w") self.save end end