diff options
| author | Alban Peignier | 2016-11-22 16:00:24 +0100 |
|---|---|---|
| committer | Alban Peignier | 2016-11-22 16:00:24 +0100 |
| commit | 847a3bfc3ecaf0a3fe981359fe7a3eaaae3fac24 (patch) | |
| tree | ed74741b945ef562a207a0684e73e1a44de093c4 /app | |
| parent | 58dd80dee1d26890aba89354470304555a54dc29 (diff) | |
| download | chouette-core-847a3bfc3ecaf0a3fe981359fe7a3eaaae3fac24.tar.bz2 | |
Manage several ReferentialMetadata::Periods in ReferentialMetadata. Refs #2035
Diffstat (limited to 'app')
| -rw-r--r-- | app/controllers/referentials_controller.rb | 2 | ||||
| -rw-r--r-- | app/models/referential.rb | 36 | ||||
| -rw-r--r-- | app/models/referential_metadata.rb | 146 | ||||
| -rw-r--r-- | app/views/referentials/_form.html.slim | 19 | ||||
| -rw-r--r-- | app/views/referentials/_period_fields.html.slim | 6 | ||||
| -rw-r--r-- | app/views/referentials/show.html.slim | 10 |
6 files changed, 153 insertions, 66 deletions
diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb index b7e6d8031..6a7631894 100644 --- a/app/controllers/referentials_controller.rb +++ b/app/controllers/referentials_controller.rb @@ -102,7 +102,7 @@ class ReferentialsController < BreadcrumbController :archived_at, :created_from_id, :workbench_id, - metadatas_attributes: [:id, :first_period_begin, :first_period_end, :lines => []] + metadatas_attributes: [:id, :first_period_begin, :first_period_end, periods_attributes: [:begin, :end, :id, :_destroy], :lines => []] ) end 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 diff --git a/app/views/referentials/_form.html.slim b/app/views/referentials/_form.html.slim index bce8818f2..30bf99632 100644 --- a/app/views/referentials/_form.html.slim +++ b/app/views/referentials/_form.html.slim @@ -49,18 +49,19 @@ - if @referential.errors.has_key? :metadatas .row - .col-lg-12 - .alert.alert-danger - - @referential.errors[:metadatas].each do |message| - p.small = "- #{message}" = form.simple_fields_for :metadatas do |subform| .row - .col-lg-6.col-md-6.col-sm-6.col-xs-6 - = subform.input :first_period_begin, as: :date, html5: true, input_html: { style: 'width: 100%' } - .col-lg-6.col-md-6.col-sm-6.col-xs-6 - = subform.input :first_period_end, as: :date, html5: true, input_html: { style: 'width: 100%' } - + = subform.simple_fields_for :periods do |period_form| + - if period_form.object.errors.has_key? :base + .col-lg-12 + .alert.alert-danger + - period_form.object.errors[:base].each do |message| + p.small = message + = render 'period_fields', f: period_form + .links + = link_to_add_association 'Ajouter une période', subform, :periods + .row .col-lg-8.col-md-12.col-sm-12.col-xs-12 = subform.input :lines, as: :select, collection: @referential.workbench.lines.includes(:company).order(:name), selected: subform.object.line_ids, label_method: :display_name, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Sélection de lignes', 'multiple': 'multiple', style: 'width: 100%' } diff --git a/app/views/referentials/_period_fields.html.slim b/app/views/referentials/_period_fields.html.slim new file mode 100644 index 000000000..68db7be3f --- /dev/null +++ b/app/views/referentials/_period_fields.html.slim @@ -0,0 +1,6 @@ +.nested-fields + .col-lg-6.col-md-6.col-sm-6.col-xs-6 + = f.input :begin, as: :date, html5: true, input_html: { style: 'width: 100%' } + .col-lg-6.col-md-6.col-sm-6.col-xs-6 + = f.input :end, as: :date, html5: true, input_html: { style: 'width: 100%' } + = link_to_remove_association "Supprimer", f diff --git a/app/views/referentials/show.html.slim b/app/views/referentials/show.html.slim index 94c463d74..7df7efff4 100644 --- a/app/views/referentials/show.html.slim +++ b/app/views/referentials/show.html.slim @@ -27,13 +27,13 @@ h2 p label = "#{Referential.human_attribute_name('validity_period')} : " - - if Chouette::TimeTable.start_validity_period.nil? + - unless period = @referential.metadatas_period = " #{Referential.human_attribute_name('no_validity_period')}" - else - = " #{Referential.human_attribute_name('start_validity_period')}" - = l Chouette::TimeTable.start_validity_period - = Referential.human_attribute_name("end_validity_period") - = l Chouette::TimeTable.end_validity_period + => " #{Referential.human_attribute_name('start_validity_period')}" + => l period.begin + => Referential.human_attribute_name("end_validity_period") + = l period.end / - if @referential.api_keys.present? / h3.api_keys = t('.api_keys') |
