From f7fe4c24e29ceb517abdda66899952a5d6aa73bf Mon Sep 17 00:00:00 2001
From: Zog
Date: Wed, 18 Apr 2018 13:19:18 +0200
Subject: Refs #6572; New Referential#Show for noredy referentials
---
 app/controllers/clean_ups_controller.rb            |   2 +
 app/controllers/concerns/referential_support.rb    |   1 +
 app/helpers/referentials_helper.rb                 |  10 ++
 app/models/clean_up.rb                             |   4 +
 app/models/concerns/iev_interfaces/task.rb         |   4 +
 app/models/referential.rb                          |  30 +++-
 app/policies/referential_policy.rb                 |   4 +
 .../layouts/navigation/_page_header.html.slim      |   1 +
 app/views/referentials/show.html.slim              | 154 +++++++++++----------
 config/locales/clean_ups.en.yml                    |   4 +
 config/locales/clean_ups.fr.yml                    |   4 +
 config/locales/merges.fr.yml                       |  11 +-
 config/locales/referentials.en.yml                 |   7 +-
 config/locales/referentials.fr.yml                 |   7 +-
 spec/factories/imports/gtfs_imports.rb             |   7 +
 spec/models/referential_spec.rb                    |  77 ++++++++++-
 16 files changed, 242 insertions(+), 85 deletions(-)
 create mode 100644 spec/factories/imports/gtfs_imports.rb
diff --git a/app/controllers/clean_ups_controller.rb b/app/controllers/clean_ups_controller.rb
index c25df1a00..350f9ae87 100644
--- a/app/controllers/clean_ups_controller.rb
+++ b/app/controllers/clean_ups_controller.rb
@@ -3,6 +3,8 @@ class CleanUpsController < ChouetteController
   respond_to :html, :only => [:create]
   belongs_to :referential
 
+  defaults :resource_class => CleanUp
+
   def create
     @clean_up = CleanUp.new(clean_up_params)
     @clean_up.referential = @referential
diff --git a/app/controllers/concerns/referential_support.rb b/app/controllers/concerns/referential_support.rb
index fe75e3579..f606e0e63 100644
--- a/app/controllers/concerns/referential_support.rb
+++ b/app/controllers/concerns/referential_support.rb
@@ -8,6 +8,7 @@ module ReferentialSupport
   end
 
   def switch_referential
+    authorize referential, :browse?
     Apartment::Tenant.switch!(referential.slug)
   end
 
diff --git a/app/helpers/referentials_helper.rb b/app/helpers/referentials_helper.rb
index 9b5b13ace..ef8831cdc 100644
--- a/app/helpers/referentials_helper.rb
+++ b/app/helpers/referentials_helper.rb
@@ -12,6 +12,16 @@ module ReferentialsHelper
     end
   end
 
+  def referential_state referential
+    out = if referential.archived?
+      "
#{t('activerecord.attributes.referential.archived_at')}
"
+    else
+      "#{"referentials.states.#{referential.state}".t}
"
+    end
+
+    out.html_safe
+  end
+
   def referential_overview referential
     service = ReferentialOverview.new referential, self
     render partial: "referentials/overview", locals: {referential: referential, overview: service}
diff --git a/app/models/clean_up.rb b/app/models/clean_up.rb
index ca4f6312b..0f73e07b2 100644
--- a/app/models/clean_up.rb
+++ b/app/models/clean_up.rb
@@ -12,6 +12,10 @@ class CleanUp < ApplicationModel
   validate :end_date_must_be_greater_that_begin_date
   after_commit :perform_cleanup, :on => :create
 
+  scope :for_referential, ->(referential) do
+    where(referential_id: referential.id)
+  end
+
   def end_date_must_be_greater_that_begin_date
     if self.end_date && self.date_type == 'between' && self.begin_date >= self.end_date
       errors.add(:base, I18n.t('activerecord.errors.models.clean_up.invalid_period'))
diff --git a/app/models/concerns/iev_interfaces/task.rb b/app/models/concerns/iev_interfaces/task.rb
index f052b3a8f..6be33734b 100644
--- a/app/models/concerns/iev_interfaces/task.rb
+++ b/app/models/concerns/iev_interfaces/task.rb
@@ -23,6 +23,10 @@ module IevInterfaces::Task
       where('started_at BETWEEN :begin AND :end', begin: period_range.begin, end: period_range.end)
     end
 
+    scope :for_referential, ->(referential) do
+      where(referential_id: referential.id)
+    end
+
     scope :blocked, -> { where('created_at < ? AND status = ?', 4.hours.ago, 'running') }
 
     before_save :initialize_fields, on: :create
diff --git a/app/models/referential.rb b/app/models/referential.rb
index c7c22604d..2449d767d 100644
--- a/app/models/referential.rb
+++ b/app/models/referential.rb
@@ -59,12 +59,12 @@ class Referential < ApplicationModel
 
 
   scope :pending, -> { where(ready: false, failed_at: nil, archived_at: nil) }
-  scope :ready, -> { where(ready: true, failed_at: nil, archived_at: nil) }
+  scope :active, -> { where(ready: true, failed_at: nil, archived_at: nil) }
   scope :failed, -> { where.not(failed_at: nil) }
   scope :archived, -> { where.not(archived_at: nil) }
 
-  scope :ready, -> { where(ready: true, failed_at: nil, archived_at: nil) }
-  scope :ready, -> { where(ready: true, failed_at: nil, archived_at: nil) }
+  scope :ready, -> { where(ready: true) }
+
   scope :in_periode, ->(periode) { where(id: referential_ids_in_periode(periode)) }
   scope :include_metadatas_lines, ->(line_ids) { where('referential_metadata.line_ids && ARRAY[?]::bigint[]', line_ids) }
   scope :order_by_validity_period, ->(dir) { joins(:metadatas).order("unnest(periodes) #{dir}") }
@@ -122,6 +122,20 @@ class Referential < ApplicationModel
     @_models_with_checksum || []
   end
 
+  OPERATIONS = [Import::Netex, Import::Gtfs, CleanUp]
+
+  def last_operation
+    operations = []
+    Referential::OPERATIONS.each do |klass|
+      operations << klass.for_referential(self).limit(1).select("'#{klass.name}' as kind, id, created_at").order('created_at DESC').to_sql
+    end
+    sql = "SELECT * FROM ((#{operations.join(') UNION (')})) AS subquery ORDER BY subquery.created_at DESC"
+    res = ActiveRecord::Base.connection.execute(sql).first
+    if res
+      res["kind"].constantize.find(res["id"])
+    end
+  end
+
   def lines
     if metadatas.blank?
       workbench ? workbench.lines : associated_lines
@@ -538,7 +552,7 @@ class Referential < ApplicationModel
   def state
     return :failed if failed_at.present?
     return :archived if archived_at.present?
-    ready ? :ready : :pending
+    ready? ? :active : :pending
   end
 
   def pending!
@@ -549,7 +563,7 @@ class Referential < ApplicationModel
     update ready: false, failed_at: Time.now, archived_at: nil
   end
 
-  def ready!
+  def active!
     update ready: true, failed_at: nil, archived_at: nil
   end
 
@@ -557,6 +571,12 @@ class Referential < ApplicationModel
     update failed_at: nil, archived_at: Time.now
   end
 
+  %i(pending active failed archived).each do |s|
+    define_method "#{s}?" do
+      state == s
+    end
+  end
+
   def pending_while
     vals = attributes.slice(*%w(ready archived_at failed_at))
     pending!
diff --git a/app/policies/referential_policy.rb b/app/policies/referential_policy.rb
index f5c2d7c08..e86da7c35 100644
--- a/app/policies/referential_policy.rb
+++ b/app/policies/referential_policy.rb
@@ -5,6 +5,10 @@ class ReferentialPolicy < ApplicationPolicy
     end
   end
 
+  def browse?
+    record.ready? || record.archived?
+  end
+
   def create?
     user.has_permission?('referentials.create')
   end
diff --git a/app/views/layouts/navigation/_page_header.html.slim b/app/views/layouts/navigation/_page_header.html.slim
index bdc4bdfed..1cac86c4a 100644
--- a/app/views/layouts/navigation/_page_header.html.slim
+++ b/app/views/layouts/navigation/_page_header.html.slim
@@ -1,5 +1,6 @@
 - action_links = resource.action_links(params[:action]) rescue nil
 - action_links ||= decorated_collection.action_links(params[:action]) rescue nil
+- action_links = nil if response.status != 200
 .page_header
   .container-fluid
     .row
diff --git a/app/views/referentials/show.html.slim b/app/views/referentials/show.html.slim
index 3cdcff63b..5849bc582 100644
--- a/app/views/referentials/show.html.slim
+++ b/app/views/referentials/show.html.slim
@@ -6,88 +6,100 @@
     .row
       .col-lg-6.col-md-6.col-sm-12.col-xs-12
         - attributes = {}
-        - attributes[@referential.human_attribute_name(:status)] = @referential.referential_read_only? ? "#{t('activerecord.attributes.referential.archived_at')}
".html_safe : "#{t('activerecord.attributes.referential.archived_at_null')}
".html_safe unless @referential.in_referential_suite?
+        - attributes[@referential.human_attribute_name(:state)] = referential_state(@referential) unless @referential.in_referential_suite?
         - attributes[@referential.human_attribute_name(:validity_period)] = (@referential.validity_period.present? ? t('validity_range', debut: l(@referential.try(:validity_period).try(:begin), format: :short), end: l(@referential.try(:validity_period).try(:end), format: :short)) : '-')
         - attributes[@referential.human_attribute_name(:organisation)] = @referential.organisation.name
         - attributes[@referential.human_attribute_name(:merged_at)] = @referential.merged_at ? l(@referential.merged_at, format: :short) : '-' unless @referential.in_referential_suite?
         = definition_list t('metadatas'), attributes
 
-    - if params[:q].present? or @reflines.any?
-      .row
-        .col-lg-12
-          = render 'filters'
+    - unless @referential.ready?
+      - operation = @referential.last_operation
+      .jumbotron
+          = import_status(operation.status)
+          - url = operation.is_a?(Import::Base) ? [operation.workbench, operation.parent || operation] : [@referential, operation]
+          = link_to url do
+            |  
+            span
+              strong= operation.try(:name) || operation.created_at.l(format: :short)
+            |  
+            span= "(#{operation.class.ts})"
 
-    - if @reflines.any?
-      .row
-        .col-lg-12
-          / ID Codif, nom court, nom de la ligne, réseau, mode, transporteur principal, actions = [show, edit_notes]
-          = table_builder_2 @reflines,
-            [ \
-              TableBuilderHelper::Column.new( \
-                name: t('id_codif'), \
-                attribute: Proc.new { |n| n.get_objectid.short_id }, \
-                sortable: false \
-              ), \
-              TableBuilderHelper::Column.new( \
-                key: :number, \
-                attribute: 'number' \
-              ), \
-              TableBuilderHelper::Column.new( \
-                key: :name, \
-                attribute: 'name', \
-                link_to: lambda do |line| \
-                  referential_line_path(@referential, line) \
-                end \
-              ), \
-              TableBuilderHelper::Column.new( \
-                key: :status, \
-                attribute: Proc.new { |n| line_status(n.status) } \
-              ), \
-              TableBuilderHelper::Column.new( \
-                key: :transport_mode, \
-                attribute: Proc.new { |n| n.transport_mode ? t("enumerize.transport_mode.#{n.transport_mode}") : '' }, \
-              ), \
-              TableBuilderHelper::Column.new( \
-                key: 'networks.name', \
-                attribute: Proc.new { |n| n.try(:network).try(:name) } \
-              ), \
-              TableBuilderHelper::Column.new( \
-                key: 'companies.name', \
-                attribute: Proc.new { |n| n&.company&.name || "-" } \
-              ) \
-            ],
-            cls: 'table has-filter has-search',
-            action: :index
+    - else
+      - if params[:q].present? or @reflines.any?
+        .row
+          .col-lg-12
+            = render 'filters'
+      - if @reflines.any?
+        .row
+          .col-lg-12
+            / ID Codif, nom court, nom de la ligne, réseau, mode, transporteur principal, actions = [show, edit_notes]
+            = table_builder_2 @reflines,
+              [ \
+                TableBuilderHelper::Column.new( \
+                  name: t('id_codif'), \
+                  attribute: Proc.new { |n| n.get_objectid.short_id }, \
+                  sortable: false \
+                ), \
+                TableBuilderHelper::Column.new( \
+                  key: :number, \
+                  attribute: 'number' \
+                ), \
+                TableBuilderHelper::Column.new( \
+                  key: :name, \
+                  attribute: 'name', \
+                  link_to: lambda do |line| \
+                    referential_line_path(@referential, line) \
+                  end \
+                ), \
+                TableBuilderHelper::Column.new( \
+                  key: :status, \
+                  attribute: Proc.new { |n| line_status(n.status) } \
+                ), \
+                TableBuilderHelper::Column.new( \
+                  key: :transport_mode, \
+                  attribute: Proc.new { |n| n.transport_mode ? t("enumerize.transport_mode.#{n.transport_mode}") : '' }, \
+                ), \
+                TableBuilderHelper::Column.new( \
+                  key: 'networks.name', \
+                  attribute: Proc.new { |n| n.try(:network).try(:name) } \
+                ), \
+                TableBuilderHelper::Column.new( \
+                  key: 'companies.name', \
+                  attribute: Proc.new { |n| n&.company&.name || "-" } \
+                ) \
+              ],
+              cls: 'table has-filter has-search',
+              action: :index
 
-          = new_pagination @reflines, 'pull-right'
+            = new_pagination @reflines, 'pull-right'
 
-    - unless @reflines.any?
-      .row.mt-xs
-        .col-lg-12
-          = replacement_msg t('referential_lines.search_no_results')
+      - unless @reflines.any?
+        .row.mt-xs
+          .col-lg-12
+            = replacement_msg t('referential_lines.search_no_results')
 
 
-    = referential_overview resource
+      = referential_overview resource
 
-/ Modal(s)
-= modalbox 'purgeModal' do
-  = simple_form_for [@referential, CleanUp.new] do |f|
-    .modal-header
-      h4.modal-title #{t('simple_form.labels.clean_up.title')}
-    .modal-body
-      .container-fluid
-        .row
-          .col-lg-8.col-ld-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2.col-xs-12
-            = f.input :date_type, as: :radio_buttons, label: false
+  / Modal(s)
+  = modalbox 'purgeModal' do
+    = simple_form_for [@referential, CleanUp.new] do |f|
+      .modal-header
+        h4.modal-title #{t('simple_form.labels.clean_up.title')}
+      .modal-body
+        .container-fluid
+          .row
+            .col-lg-8.col-ld-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2.col-xs-12
+              = f.input :date_type, as: :radio_buttons, label: false
 
-          .col-lg-8.col-ld-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2.col-xs-12
-            label.control-label.begin_date = t('titles.clean_up.begin_date')
-            label.control-label.end_date.hidden = t('titles.clean_up.end_date')
-            = f.input :begin_date, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
+            .col-lg-8.col-ld-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2.col-xs-12
+              label.control-label.begin_date = t('titles.clean_up.begin_date')
+              label.control-label.end_date.hidden = t('titles.clean_up.end_date')
+              = f.input :begin_date, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
 
-            = f.input :end_date, as: :date, label: t('titles.clean_up.end_date'), wrapper_html: { class: 'date cleanup_end_date_wrapper smart_date', id: "end_date" }
+              = f.input :end_date, as: :date, label: t('titles.clean_up.end_date'), wrapper_html: { class: 'date cleanup_end_date_wrapper smart_date', id: "end_date" }
 
-    .modal-footer
-      button.btn.btn-link type='button' data-dismiss='modal' #{t('cancel')}
-      - unless policy(@referential).referential_read_only?
-        = f.button :submit, t('actions.clean_up') , class: 'btn btn-primary'
+      .modal-footer
+        button.btn.btn-link type='button' data-dismiss='modal' #{t('cancel')}
+        - unless policy(@referential).referential_read_only?
+          = f.button :submit, t('actions.clean_up') , class: 'btn btn-primary'
diff --git a/config/locales/clean_ups.en.yml b/config/locales/clean_ups.en.yml
index 6cbb2c453..b80551cc0 100644
--- a/config/locales/clean_ups.en.yml
+++ b/config/locales/clean_ups.en.yml
@@ -23,6 +23,10 @@ en:
         begin_date: "Begin date of clean up"
         end_date: "End date of clean up"
   activerecord:
+    models:
+      clean_up:
+        one: Cleanup
+        other: Cleanups
     errors:
       models:
         clean_up:
diff --git a/config/locales/clean_ups.fr.yml b/config/locales/clean_ups.fr.yml
index 59d7c5dbc..0c8445875 100644
--- a/config/locales/clean_ups.fr.yml
+++ b/config/locales/clean_ups.fr.yml
@@ -22,6 +22,10 @@ fr:
         begin_date: "Date de début de la purge"
         end_date: "Date de fin de la purge"
   activerecord:
+    models:
+      clean_up:
+        one: Purge
+        other: Purges
     errors:
       models:
         clean_up:
diff --git a/config/locales/merges.fr.yml b/config/locales/merges.fr.yml
index 345727b74..1bb03200c 100644
--- a/config/locales/merges.fr.yml
+++ b/config/locales/merges.fr.yml
@@ -9,9 +9,18 @@ fr:
       title: "Finalisation de l'offre %{name}"
     actions:
       create: Finaliser des Jeux de Données
+    statuses:
+      new: Nouveau
+      pending: En attente
+      successful: Succès
+      failed: Erreur
+      running: En cours
   activerecord:
     models:
-      merge: "Finalisation de l'offre"
+      merge:
+        zero:  "Finalisations d'offre"
+        one:   "Finalisation de l'offre"
+        other: "Finalisations d'offre"
     attributes:
       merge:
         created_at: "Créé le"
diff --git a/config/locales/referentials.en.yml b/config/locales/referentials.en.yml
index f52eaa1cb..46b8e5680 100644
--- a/config/locales/referentials.en.yml
+++ b/config/locales/referentials.en.yml
@@ -55,6 +55,11 @@ en:
         today: Today
         prev_page: Prev. page
         next_page: Next page
+    states:
+      pending:  Pending
+      failed:   Failed
+      ready:    Ready
+      archived: Archived
 
   activerecord:
     models:
@@ -65,7 +70,7 @@ en:
     attributes:
       referential:
         name: "Name"
-        status: "Status"
+        state: "Status"
         slug: "Code"
         prefix: "Neptune Object Id prefix"
         projection_type: "Optional spatial reference system code (SRID)"
diff --git a/config/locales/referentials.fr.yml b/config/locales/referentials.fr.yml
index c4633014b..2c1e55445 100644
--- a/config/locales/referentials.fr.yml
+++ b/config/locales/referentials.fr.yml
@@ -55,6 +55,11 @@ fr:
         today: Aujourd'hui
         prev_page: ←
         next_page: →
+    states:
+      pending:  En cours de traitement
+      failed:   En erreur
+      ready:    En édition
+      archived: Archivé
 
   activerecord:
     models:
@@ -65,7 +70,7 @@ fr:
     attributes:
       referential:
         name: "Nom"
-        status: "Etat"
+        state: "Etat"
         slug: "Code"
         prefix: "Préfixe des identifiants Neptune"
         projection_type: "Système de référence spatiale (SRID) optionnel"
diff --git a/spec/factories/imports/gtfs_imports.rb b/spec/factories/imports/gtfs_imports.rb
new file mode 100644
index 000000000..b91e1ee5f
--- /dev/null
+++ b/spec/factories/imports/gtfs_imports.rb
@@ -0,0 +1,7 @@
+FactoryGirl.define do
+  factory :gtfs_import, class: Import::Gtfs, parent: :import do
+    file { File.open(Rails.root.join('spec', 'fixtures', 'OFFRE_TRANSDEV_2017030112251.zip')) }
+    association :parent, factory: :workbench_import
+
+  end
+end
diff --git a/spec/models/referential_spec.rb b/spec/models/referential_spec.rb
index 220201d37..8b8cf4359 100644
--- a/spec/models/referential_spec.rb
+++ b/spec/models/referential_spec.rb
@@ -29,6 +29,71 @@ describe Referential, :type => :model do
     end
   end
 
+  context ".last_operation" do
+    subject(:operation){ referential.last_operation }
+    it "should return nothing" do
+      expect(operation).to be_nil
+    end
+
+    context "with a netex import" do
+      let!(:import) do
+        import = create :netex_import
+        import.referential = referential
+        import.save
+        import
+      end
+
+      it "should return the import" do
+        expect(operation).to eq import
+      end
+    end
+
+    context "with 2 netex imports" do
+      let!(:other_import) do
+        import = create :netex_import
+        import.referential = referential
+        import.save
+        import
+      end
+      let!(:import) do
+        import = create :netex_import
+        import.referential = referential
+        import.save
+        import
+      end
+
+      it "should return the last import" do
+        expect(operation).to eq import
+      end
+    end
+
+    context "with a gtfs import" do
+      let!(:import) do
+        import = create :gtfs_import
+        import.referential = referential
+        import.save
+        import
+      end
+
+      it "should return the import" do
+        expect(operation).to eq import
+      end
+    end
+
+    context "with a cleanup" do
+      let!(:cleanup) do
+        cleanup = create :clean_up
+        cleanup.referential = referential
+        cleanup.save
+        cleanup
+      end
+
+      it "should return the cleanup" do
+        expect(operation).to eq cleanup
+      end
+    end
+  end
+
   context ".state" do
     it "should return the expected values" do
       referential = build :referential
@@ -38,7 +103,7 @@ describe Referential, :type => :model do
       expect(referential.state).to eq :failed
       referential.ready = true
       referential.failed_at = nil
-      expect(referential.state).to eq :ready
+      expect(referential.state).to eq :active
       referential.archived_at = Time.now
       expect(referential.state).to eq :archived
     end
@@ -48,28 +113,28 @@ describe Referential, :type => :model do
         referential = create :referential, ready: false
         expect(Referential.pending).to include referential
         expect(Referential.failed).to_not include referential
-        expect(Referential.ready).to_not include referential
+        expect(Referential.active).to_not include referential
         expect(Referential.archived).to_not include referential
 
         referential = create :referential
         referential.failed!
         expect(Referential.pending).to_not include referential
         expect(Referential.failed).to include referential
-        expect(Referential.ready).to_not include referential
+        expect(Referential.active).to_not include referential
         expect(Referential.archived).to_not include referential
 
         referential = create :referential
-        referential.ready!
+        referential.active!
         expect(Referential.pending).to_not include referential
         expect(Referential.failed).to_not include referential
-        expect(Referential.ready).to include referential
+        expect(Referential.active).to include referential
         expect(Referential.archived).to_not include referential
 
         referential = create :referential
         referential.archived!
         expect(Referential.pending).to_not include referential
         expect(Referential.failed).to_not include referential
-        expect(Referential.ready).to_not include referential
+        expect(Referential.active).to_not include referential
         expect(Referential.archived).to include referential
       end
     end
-- 
cgit v1.2.3