diff options
| author | Alban Peignier | 2016-11-22 18:31:14 +0100 |
|---|---|---|
| committer | Alban Peignier | 2016-11-22 18:31:14 +0100 |
| commit | a14c8afc2df1d6c7ac186404fa4e1e86cef6a811 (patch) | |
| tree | f0abb6bbaffef4962cb1fe69c97dd0948ad9553f /app/models | |
| parent | 85daced332703d1447340c5fc17972a662b4080c (diff) | |
| parent | 7d58f83b1c38883a79258598b57d76c5f16fb807 (diff) | |
| download | chouette-core-a14c8afc2df1d6c7ac186404fa4e1e86cef6a811.tar.bz2 | |
Merge branch 'metadata_multiple_periods' into staging
Diffstat (limited to 'app/models')
| -rw-r--r-- | app/models/referential.rb | 36 | ||||
| -rw-r--r-- | app/models/referential_metadata.rb | 146 |
2 files changed, 131 insertions, 51 deletions
diff --git a/app/models/referential.rb b/app/models/referential.rb index b75ff3ab9..1a4b543b7 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -204,34 +204,44 @@ class Referential < ActiveRecord::Base end def metadatas_period - # FIXME - if metadatas.present? - metadatas.first.periodes.try :first + query = "select min(lower), max(upper) from (select lower(unnest(periodes)) as lower, upper(unnest(periodes)) as upper from public.referential_metadata where public.referential_metadata.referential_id = #{id}) bounds;" + + row = self.class.connection.select_one(query) + lower, upper = row["min"], row["max"] + + if lower and upper + Range.new(Date.parse(lower), Date.parse(upper)-1) end end alias_method :validity_period, :metadatas_period def metadatas_lines - # FIXME - metadatas.present? ? metadatas.first.lines : [] + if metadatas.present? + scope = workbench ? workbench.lines : associated_lines + scope.where(id: metadatas.pluck(:line_ids).flatten) + else + Chouete::Line.none + end end def overlapped_referential_ids return [] unless metadatas.present? line_ids = metadatas.first.line_ids - period = metadatas.first.periodes.try :first + periodes = metadatas.first.periodes - return [] unless line_ids.present? && period + return [] unless line_ids.present? && periodes.present? not_myself = "and referential_id != #{id}" if persisted? - query = "SELECT distinct(referential_id) FROM - (SELECT unnest(public.referential_metadata.line_ids) as line, unnest(public.referential_metadata.periodes) as period, public.referential_metadata.referential_id - FROM public.referential_metadata - INNER JOIN public.referentials ON public.referential_metadata.referential_id = public.referentials.id - WHERE public.referentials.workbench_id = #{workbench_id} and public.referentials.archived_at is null) as metadatas - WHERE line in (#{line_ids.join(',')}) and period && '#{ActiveRecord::ConnectionAdapters::PostgreSQLColumn.range_to_string(period)}' #{not_myself};" + periods_query = periodes.map do |periode| + "period && '#{ActiveRecord::ConnectionAdapters::PostgreSQLColumn.range_to_string(periode)}'" + end.join(" OR ") + + query = "select distinct(public.referential_metadata.referential_id) FROM public.referential_metadata, unnest(line_ids) line, LATERAL unnest(periodes) period + WHERE public.referential_metadata.referential_id + IN (SELECT public.referentials.id FROM public.referentials WHERE referentials.workbench_id = #{workbench_id} and referentials.archived_at is null #{not_myself}) + AND line in (#{line_ids.join(',')}) and (#{periods_query});" self.class.connection.select_values(query).map(&:to_i) end diff --git a/app/models/referential_metadata.rb b/app/models/referential_metadata.rb index a1066e43b..0c792a9b9 100644 --- a/app/models/referential_metadata.rb +++ b/app/models/referential_metadata.rb @@ -7,68 +7,138 @@ class ReferentialMetadata < ActiveRecord::Base validates :lines, presence: true validates :periodes, presence: true - validates :first_period_begin, :first_period_end, presence: true - scope :include_lines, -> (line_ids) { where('line_ids && ARRAY[?]', line_ids) } scope :include_dateranges, -> (dateranges) { where('periodes && ARRAY[?]', dateranges) } - def first_period - periodes.first if periodes + class Period + include ActiveAttr::Model + + attribute :id, type: Integer + attribute :begin, type: Date + attribute :end, type: Date + + validates :begin, :end, presence: true + validate :check_end_greather_than_begin + + def check_end_greather_than_begin + if self.begin and self.end and self.begin > self.end + errors.add(:end, :invalid) + end + end + + def self.from_range(index, range) + Period.new id: index, begin: range.begin, end: range.end + end + + def range + if self.begin and self.end and self.begin <= self.end + Range.new self.begin, self.end + end + end + + def intersect?(*other) + return false if range.nil? + + other = other.flatten + other = other.delete_if { |o| o.id == id } if id + + other.any? do |period| + if other_range = period.range + (range & other_range).present? + end + end + end + + # Stuff required for coocon + def new_record? + !persisted? + end + def persisted? + id.present? + end + + + def mark_for_destruction + self._destroy = true + end + + attribute :_destroy, type: Boolean + alias_method :marked_for_destruction?, :_destroy end - def first_period_begin - @first_period_begin or first_period.try(:begin) + # Required by coocon + def build_period + Period.new end - def first_period_begin=(date) - date = (Date.parse(date) rescue nil) if String === date - periodes_will_change! unless @first_period_begin == date - @first_period_begin = date + + def periods + @periods ||= init_periods end - def first_period_end - if @first_period_end - @first_period_end + + def init_periods + if periodes + periodes.each_with_index.map { |r, index| Period.from_range(index, r) } else - if first_period - date = first_period.end - date -= 1 if first_period.exclude_end? - date - end + [] end end - def first_period_end=(date) - date = (Date.parse(date) rescue nil) if String === date - periodes_will_change! unless @first_period_end == date - @first_period_end = date - end + private :init_periods - validate :check_first_period_end + validate :validate_periods - def check_first_period_end - if @first_period_begin and @first_period_end and @first_period_begin > @first_period_end - errors.add(:first_period_end, :invalid) + def validate_periods + Rails.logger.debug "Validate periods" + unless periods.all?(&:valid?) + errors.add(:periods, :invalid) end - end - before_validation :set_first_period + periods.each do |period| + Rails.logger.debug "Validate period #{period.inspect} : #{period.intersect?(periods)}" + if period.intersect?(periods) + period.errors.add(:base, :invalid) + Rails.logger.debug "period errors #{period.errors}" + end + end + end - def set_first_period - if @first_period_begin and @first_period_end and @first_period_begin <= @first_period_end - self.periodes ||= [] - self.periodes[0] = Range.new @first_period_begin, @first_period_end + def periods_attributes=(attributes = {}) + @periods = [] + attributes.each do |index, period_attribute| + period = Period.new(period_attribute.merge(id: index)) + @periods << period unless period.marked_for_destruction? end + + # if self.periodes != @periods.map(&:range).compact + periodes_will_change! + # end end - def column_for_attribute(name) - if %i{first_period_begin first_period_end}.include?(name.to_sym) - ActiveRecord::ConnectionAdapters::Column.new(name, nil, "date") - else - super name + before_validation :fill_periodes + + def fill_periodes + if @periods + self.periodes = @periods.map(&:range).compact.sort_by(&:begin) end end + after_save :clear_periods + + def clear_periods + @periods = nil + end + private :clear_periods + def self.new_from from from.dup.tap do |metadata| metadata.referential_id = nil end end end + +class Range + def intersection(other) + return nil if (self.max < other.begin or other.max < self.begin) + [self.begin, other.begin].max..[self.max, other.max].min + end + alias_method :&, :intersection +end |
