diff options
Diffstat (limited to 'app/models')
| -rw-r--r-- | app/models/calendar.rb | 83 | ||||
| -rw-r--r-- | app/models/calendar/period.rb | 5 | ||||
| -rw-r--r-- | app/models/chouette/time_table.rb | 191 | ||||
| -rw-r--r-- | app/models/concerns/application_days_support.rb | 107 | ||||
| -rw-r--r-- | app/models/concerns/date_support.rb | 11 | ||||
| -rw-r--r-- | app/models/concerns/timetable_support.rb | 148 |
6 files changed, 380 insertions, 165 deletions
diff --git a/app/models/calendar.rb b/app/models/calendar.rb index a7fd9220c..561a2e3f7 100644 --- a/app/models/calendar.rb +++ b/app/models/calendar.rb @@ -5,6 +5,8 @@ require_relative 'calendar/period' class Calendar < ActiveRecord::Base include DateSupport include PeriodSupport + include ApplicationDaysSupport + include TimetableSupport has_paper_trail class_name: 'PublicVersion' belongs_to :organisation @@ -16,10 +18,28 @@ class Calendar < ActiveRecord::Base scope :contains_date, ->(date) { where('date ? = any (dates) OR date ? <@ any (date_ranges)', date, date) } + after_initialize :set_defaults + def self.ransackable_scopes(auth_object = nil) [:contains_date] end + def self.state_permited_attributes item + {name: item["comment"]} + end + + def set_defaults + self.excluded_dates ||= [] + self.int_day_types ||= EVERYDAY + end + + def human_attribute_name(*args) + self.class.human_attribute_name(*args) + end + + def shortcuts_update(date=nil) + end + def convert_to_time_table Chouette::TimeTable.new.tap do |tt| self.dates.each do |d| @@ -28,8 +48,69 @@ class Calendar < ActiveRecord::Base self.periods.each do |p| tt.periods << Chouette::TimeTablePeriod.new(period_start: p.begin, period_end: p.end) end - tt.int_day_types = 508 + tt.int_day_types = self.int_day_types + end + end + + def include_in_dates?(day) + self.dates.include? day + end + + def excluded_date?(day) + self.excluded_dates.include? day + end + + def update_in_out date, in_out + if in_out + self.excluded_dates.delete date + self.dates << date unless include_in_dates?(date) + else + self.dates.delete date + self.excluded_dates << date unless excluded_date?(date) + end + date + end + + def included_days + dates + end + + def excluded_days + excluded_dates + end + + def saved_dates + Hash[*self.dates.each_with_index.to_a.map(&:reverse).flatten] + end + + def all_dates + (dates + excluded_dates).sort.each_with_index.map do |d, i| + OpenStruct.new(id: i, date: d, in_out: include_in_dates?(d)) end end + def find_date_by_id id + self.dates[id] + end + + def destroy_date date + self.dates -= [date] + end + + def create_date in_out:, date: + update_in_out date, in_out + end + + def find_period_by_id id + self.periods.find{|p| p.id == id} + end + + def build_period + self.periods << Calendar::Period.new(id: self.periods.count + 1) + self.periods.last + end + + def destroy_period period + @periods = self.periods.select{|p| p.end != period.end || p.begin != period.begin} + end end diff --git a/app/models/calendar/period.rb b/app/models/calendar/period.rb index 56ab722fe..8b3e4109b 100644 --- a/app/models/calendar/period.rb +++ b/app/models/calendar/period.rb @@ -10,6 +10,11 @@ class Calendar < ActiveRecord::Base validates_presence_of :begin, :end validate :check_end_greather_than_begin + alias_method :period_start, :begin + alias_method :period_end, :end + alias_method :period_start=, :begin= + alias_method :period_end=, :end= + def check_end_greather_than_begin if self.begin && self.end && self.begin >= self.end errors.add(:base, I18n.t('calendars.errors.short_period')) diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb index 07bf35444..8113457ec 100644 --- a/app/models/chouette/time_table.rb +++ b/app/models/chouette/time_table.rb @@ -4,11 +4,13 @@ module Chouette include ChecksumSupport include TimeTableRestrictions include ObjectidSupport + include ApplicationDaysSupport + include TimetableSupport + # FIXME http://jira.codehaus.org/browse/JRUBY-6358 self.primary_key = "id" acts_as_taggable - attr_accessor :monday,:tuesday,:wednesday,:thursday,:friday,:saturday,:sunday attr_accessor :tag_search def self.ransackable_attributes auth_object = nil @@ -81,72 +83,36 @@ module Chouette end end - def state_update state - update_attributes(self.class.state_permited_attributes(state)) - self.tag_list = state['tags'].collect{|t| t['name']}.join(', ') - self.calendar_id = nil unless state['calendar'] - - days = state['day_types'].split(',') - Date::DAYNAMES.map(&:underscore).each do |name| - prefix = human_attribute_name(name).first(2) - send("#{name}=", days.include?(prefix)) - end - - saved_dates = Hash[self.dates.collect{ |d| [d.id, d.date]}] - cmonth = Date.parse(state['current_periode_range']) - - state['current_month'].each do |d| - date = Date.parse(d['date']) - checked = d['include_date'] || d['excluded_date'] - in_out = d['include_date'] ? true : false - - date_id = saved_dates.key(date) - time_table_date = self.dates.find(date_id) if date_id + def find_date_by_id id + self.dates.find id + end - next if !checked && !time_table_date - # Destroy date if no longer checked - next if !checked && time_table_date.destroy + def destroy_date date + date.destroy + end - # Create new date - unless time_table_date - time_table_date = self.dates.create({in_out: in_out, date: date}) - end - # Update in_out - if in_out != time_table_date.in_out - time_table_date.update_attributes({in_out: in_out}) - end + def update_in_out date, in_out + if in_out != date.in_out + date.update_attributes({in_out: in_out}) end - - self.state_update_periods state['time_table_periods'] - self.save end - def state_update_periods state_periods - state_periods.each do |item| - period = self.periods.find(item['id']) if item['id'] - next if period && item['deleted'] && period.destroy - period ||= self.periods.build - - period.period_start = Date.parse(item['period_start']) - period.period_end = Date.parse(item['period_end']) + def find_period_by_id id + self.periods.find id + end - if period.changed? - period.save - item['id'] = period.id - end - end + def build_period + periods.build + end - state_periods.delete_if {|item| item['deleted']} + def destroy_period period + period.destroy end def self.state_permited_attributes item item.slice('comment', 'color').to_hash end - def presenter - @presenter ||= ::TimeTablePresenter.new( self) - end - def self.start_validity_period [Chouette::TimeTable.minimum(:start_date)].compact.min end @@ -167,20 +133,6 @@ module Chouette self.save end - def month_inspect(date) - (date.beginning_of_month..date.end_of_month).map do |d| - { - day: I18n.l(d, format: '%A'), - date: d.to_s, - wday: d.wday, - wnumber: d.strftime("%W").to_s, - mday: d.mday, - include_date: include_in_dates?(d), - excluded_date: excluded_date?(d) - } - end - end - def save_shortcuts shortcuts_update self.update_column(:start_date, start_date) @@ -314,98 +266,6 @@ module Chouette [bounding_min, bounding_max].compact end - def display_day_types - %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| self.send(d) }.map{ |d| self.human_attribute_name(d).first(2)}.join(', ') - end - - def day_by_mask(flag) - int_day_types & flag == flag - end - - def self.day_by_mask(int_day_types,flag) - int_day_types & flag == flag - end - - - def valid_days - # Build an array with day of calendar week (1-7, Monday is 1). - [].tap do |valid_days| - valid_days << 1 if monday - valid_days << 2 if tuesday - valid_days << 3 if wednesday - valid_days << 4 if thursday - valid_days << 5 if friday - valid_days << 6 if saturday - valid_days << 7 if sunday - end - end - - def self.valid_days(int_day_types) - # Build an array with day of calendar week (1-7, Monday is 1). - [].tap do |valid_days| - valid_days << 1 if day_by_mask(int_day_types,4) - valid_days << 2 if day_by_mask(int_day_types,8) - valid_days << 3 if day_by_mask(int_day_types,16) - valid_days << 4 if day_by_mask(int_day_types,32) - valid_days << 5 if day_by_mask(int_day_types,64) - valid_days << 6 if day_by_mask(int_day_types,128) - valid_days << 7 if day_by_mask(int_day_types,256) - end - end - - def monday - day_by_mask(4) - end - def tuesday - day_by_mask(8) - end - def wednesday - day_by_mask(16) - end - def thursday - day_by_mask(32) - end - def friday - day_by_mask(64) - end - def saturday - day_by_mask(128) - end - def sunday - day_by_mask(256) - end - - def set_day(day,flag) - if day == '1' || day == true - self.int_day_types |= flag - else - self.int_day_types &= ~flag - end - shortcuts_update - end - - def monday=(day) - set_day(day,4) - end - def tuesday=(day) - set_day(day,8) - end - def wednesday=(day) - set_day(day,16) - end - def thursday=(day) - set_day(day,32) - end - def friday=(day) - set_day(day,64) - end - def saturday=(day) - set_day(day,128) - end - def sunday=(day) - set_day(day,256) - end - def effective_days_of_period(period,valid_days=self.valid_days) days = [] period.period_start.upto(period.period_end) do |date| @@ -452,6 +312,17 @@ module Chouette days.sort end + def create_date in_out:, date: + self.dates.create in_out: in_out, date: date + end + + def saved_dates + Hash[self.dates.collect{ |d| [d.id, d.date]}] + end + + def all_dates + dates + end # produce a copy of periods without anyone overlapping or including another def optimize_overlapping_periods diff --git a/app/models/concerns/application_days_support.rb b/app/models/concerns/application_days_support.rb new file mode 100644 index 000000000..348436aa4 --- /dev/null +++ b/app/models/concerns/application_days_support.rb @@ -0,0 +1,107 @@ +module ApplicationDaysSupport + extend ActiveSupport::Concern + + MONDAY = 4 + TUESDAY = 8 + WEDNESDAY = 16 + THURSDAY = 32 + FRIDAY = 64 + SATURDAY = 128 + SUNDAY = 256 + EVERYDAY = MONDAY | TUESDAY | WEDNESDAY | THURSDAY | FRIDAY | SATURDAY | SUNDAY + + def display_day_types + %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| self.send(d) }.map{ |d| self.human_attribute_name(d).first(2)}.join(', ') + end + + def day_by_mask(flag) + int_day_types & flag == flag + end + + def self.day_by_mask(int_day_types,flag) + int_day_types & flag == flag + end + + def valid_days + # Build an array with day of calendar week (1-7, Monday is 1). + [].tap do |valid_days| + valid_days << 1 if monday + valid_days << 2 if tuesday + valid_days << 3 if wednesday + valid_days << 4 if thursday + valid_days << 5 if friday + valid_days << 6 if saturday + valid_days << 7 if sunday + end + end + + def valid_day? wday + valid_days.include?(wday) + end + + def self.valid_days(int_day_types) + # Build an array with day of calendar week (1-7, Monday is 1). + [].tap do |valid_days| + valid_days << 1 if day_by_mask(int_day_types,MONDAY) + valid_days << 2 if day_by_mask(int_day_types,TUESDAY) + valid_days << 3 if day_by_mask(int_day_types,WEDNESDAY) + valid_days << 4 if day_by_mask(int_day_types,THURSDAY) + valid_days << 5 if day_by_mask(int_day_types,FRIDAY) + valid_days << 6 if day_by_mask(int_day_types,SATURDAY) + valid_days << 7 if day_by_mask(int_day_types,SUNDAY) + end + end + + def monday + day_by_mask(MONDAY) + end + def tuesday + day_by_mask(TUESDAY) + end + def wednesday + day_by_mask(WEDNESDAY) + end + def thursday + day_by_mask(THURSDAY) + end + def friday + day_by_mask(FRIDAY) + end + def saturday + day_by_mask(SATURDAY) + end + def sunday + day_by_mask(SUNDAY) + end + + def set_day(day,flag) + if day == '1' || day == true + self.int_day_types |= flag + else + self.int_day_types &= ~flag + end + shortcuts_update + end + + def monday=(day) + set_day(day,4) + end + def tuesday=(day) + set_day(day,8) + end + def wednesday=(day) + set_day(day,16) + end + def thursday=(day) + set_day(day,32) + end + def friday=(day) + set_day(day,64) + end + def saturday=(day) + set_day(day,128) + end + def sunday=(day) + set_day(day,256) + end +end diff --git a/app/models/concerns/date_support.rb b/app/models/concerns/date_support.rb index fbfe19af1..5c66cb1a9 100644 --- a/app/models/concerns/date_support.rb +++ b/app/models/concerns/date_support.rb @@ -38,9 +38,12 @@ module DateSupport date_values_are_valid = false end date_ranges.each do |date_range| - if date_range.cover? date_value.value - date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_date_ranges')) - date_values_are_valid = false + if date_range.cover?(date_value.value) + excluded_day = self.respond_to?(:valid_day?) && !self.valid_day?(date_value.value.wday) + unless excluded_day + date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_date_ranges')) + date_values_are_valid = false + end end end end @@ -77,4 +80,4 @@ module DateSupport private :clear_date_values end -end
\ No newline at end of file +end diff --git a/app/models/concerns/timetable_support.rb b/app/models/concerns/timetable_support.rb new file mode 100644 index 000000000..d2bc99d51 --- /dev/null +++ b/app/models/concerns/timetable_support.rb @@ -0,0 +1,148 @@ +module TimetableSupport + extend ActiveSupport::Concern + + def presenter + @presenter ||= ::TimeTablePresenter.new( self) + end + + def periods_max_date + return nil if self.periods.empty? + + min_start = self.periods.map(&:period_start).compact.min + max_end = self.periods.map(&:period_end).compact.max + result = nil + + if max_end && min_start + max_end.downto( min_start) do |date| + if self.valid_days.include?(date.cwday) && !self.excluded_date?(date) + result = date + break + end + end + end + result + end + + def periods_min_date + return nil if self.periods.empty? + + min_start = self.periods.map(&:period_start).compact.min + max_end = self.periods.map(&:period_end).compact.max + result = nil + + if max_end && min_start + min_start.upto(max_end) do |date| + if self.valid_days.include?(date.cwday) && !self.excluded_date?(date) + result = date + break + end + end + end + result + end + + def bounding_dates + bounding_min = self.all_dates.select{|d| d.in_out}.map(&:date).compact.min + bounding_max = self.all_dates.select{|d| d.in_out}.map(&:date).compact.max + + unless self.periods.empty? + bounding_min = periods_min_date if periods_min_date && + (bounding_min.nil? || (periods_min_date < bounding_min)) + + bounding_max = periods_max_date if periods_max_date && + (bounding_max.nil? || (bounding_max < periods_max_date)) + end + + [bounding_min, bounding_max].compact + end + + def month_inspect(date) + (date.beginning_of_month..date.end_of_month).map do |d| + { + day: I18n.l(d, format: '%A'), + date: d.to_s, + wday: d.wday, + wnumber: d.strftime("%W").to_s, + mday: d.mday, + include_date: include_in_dates?(d), + excluded_date: excluded_date?(d) + } + end + end + + def include_in_dates?(day) + self.dates.any?{ |d| d.date === day && d.in_out == true } + end + + def excluded_date?(day) + self.dates.any?{ |d| d.date === day && d.in_out == false } + end + + def include_in_overlap_dates?(day) + return false if self.excluded_date?(day) + + self.all_dates.any?{ |d| d.date === day} \ + && self.periods.any?{ |period| period.period_start <= day && day <= period.period_end && valid_days.include?(day.cwday) } + end + + def include_in_periods?(day) + self.periods.any?{ |period| period.period_start <= day && + day <= period.period_end && + valid_days.include?(day.cwday) && + ! excluded_date?(day) } + end + + def state_update_periods state_periods + state_periods.each do |item| + period = self.find_period_by_id(item['id']) if item['id'] + next if period && item['deleted'] && self.destroy_period(period) + period ||= self.build_period + + period.period_start = Date.parse(item['period_start']) + period.period_end = Date.parse(item['period_end']) + period.save if period.is_a?(ActiveRecord::Base) && period.changed? + + item['id'] = period.id + end + + state_periods.delete_if {|item| item['deleted']} + end + + def state_update state + update_attributes(self.class.state_permited_attributes(state)) + self.tag_list = state['tags'].collect{|t| t['name']}.join(', ') if state['tags'] + self.calendar_id = nil if self.respond_to?(:calendar_id) && !state['calendar'] + + days = state['day_types'].split(',') + Date::DAYNAMES.map(&:underscore).each do |name| + prefix = human_attribute_name(name).first(2) + send("#{name}=", days.include?(prefix)) + end + + cmonth = Date.parse(state['current_periode_range']) + + state['current_month'].each do |d| + date = Date.parse(d['date']) + checked = d['include_date'] || d['excluded_date'] + in_out = d['include_date'] ? true : false + + date_id = saved_dates.key(date) + time_table_date = self.find_date_by_id(date_id) if date_id + + next if !checked && !time_table_date + # Destroy date if no longer checked + next if !checked && destroy_date(time_table_date) + + # Create new date + unless time_table_date + time_table_date = self.create_date in_out: in_out, date: date + end + # Update in_out + self.update_in_out time_table_date, in_out + end + + self.state_update_periods state['time_table_periods'] + self.save + end + +end |
