aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorAlban Peignier2016-11-22 16:00:24 +0100
committerAlban Peignier2016-11-22 16:00:24 +0100
commit847a3bfc3ecaf0a3fe981359fe7a3eaaae3fac24 (patch)
treeed74741b945ef562a207a0684e73e1a44de093c4 /app
parent58dd80dee1d26890aba89354470304555a54dc29 (diff)
downloadchouette-core-847a3bfc3ecaf0a3fe981359fe7a3eaaae3fac24.tar.bz2
Manage several ReferentialMetadata::Periods in ReferentialMetadata. Refs #2035
Diffstat (limited to 'app')
-rw-r--r--app/controllers/referentials_controller.rb2
-rw-r--r--app/models/referential.rb36
-rw-r--r--app/models/referential_metadata.rb146
-rw-r--r--app/views/referentials/_form.html.slim19
-rw-r--r--app/views/referentials/_period_fields.html.slim6
-rw-r--r--app/views/referentials/show.html.slim10
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')