diff --git a/app/assets/javascripts/download_releases.js b/app/assets/javascripts/download_releases.js
new file mode 100644
index 0000000..9d7726a
--- /dev/null
+++ b/app/assets/javascripts/download_releases.js
@@ -0,0 +1,20 @@
+$(document).on("click", "#download_releases", function(event) {
+ event.preventDefault();
+
+ const releasable_ids = JSON.parse($("#selected_releases_form").attr('data-releasable-ids'));
+ const total_entries = $('#total_entries').val();
+
+ const input_ids = $('').attr({ type: 'hidden', name: 'release_ids', value: JSON.stringify(releasable_ids) });
+ const search_query = $('').attr({ type: 'hidden', name: 'search_query', value: $("form input[type='search']").val() });
+ const type_filter = $('').attr({ type: 'hidden', name: 'type_filter', value: $('#type_filter_value').val() });
+
+ const download_count = releasable_ids.length > 0 ? releasable_ids.length : total_entries;
+
+ $(this).parent().append(input_ids);
+ $(this).parent().append(search_query);
+ $(this).parent().append(type_filter);
+
+ if (confirm(`${download_count} release(s) will be downloaded. Is this correct?`)){
+ Rails.fire($(this).parent()[0], 'submit');
+ }
+});
\ No newline at end of file
diff --git a/app/controllers/contract_downloads_controller.rb b/app/controllers/contract_downloads_controller.rb
index b8bade4..af242dc 100644
--- a/app/controllers/contract_downloads_controller.rb
+++ b/app/controllers/contract_downloads_controller.rb
@@ -7,28 +7,41 @@ class ContractDownloadsController < ApplicationController
def create
authorize policy_scope(Download).create
- fetch_releases
-
- download = @project.downloads.create!(release_type: params[:release_type])
+
+ download = @project.downloads.create!(release_type: release_type)
other_downloads_in_progress = @project.downloads.unfinished_desc_order.offset(1)
if other_downloads_in_progress.any?
- in_progress_downloads_details = render_to_string "_other_pending_downloads", locals: { downloads: other_downloads_in_progress, release_type: params[:release_type] }, :layout => false
+ in_progress_downloads_details = render_to_string "_other_pending_downloads", locals: { downloads: other_downloads_in_progress, release_type: release_type }, :layout => false
ProjectsChannel.broadcast_download_generation_update(download, in_progress_downloads_details)
else
- ProjectsChannel.broadcast_download_generation_update(download, I18n.t("contract_downloads.download.pending", release_type: params[:release_type].titleize))
+ ProjectsChannel.broadcast_download_generation_update(download, I18n.t("contract_downloads.download.pending", release_type: release_type.titleize))
end
- GenerateContractsZipJob.perform_later(@project, download, params[:release_type], @releases.ids)
+ GenerateContractsZipJob.perform_later(@project, download, release_type, release_ids, search_query, type_filter)
end
private
- def fetch_releases
- @releases = policy_scope(@project.public_send(releases))
+ def release_type
+ params[:release_type]
end
- def releases
- params[:release_type].constantize.model_name.plural
+ def search_query
+ params[:search_query]
+ end
+
+ def type_filter
+ params[:type_filter]
+ end
+
+ def release_ids
+ JSON.parse(params[:release_ids])
+ rescue StandardError
+ []
+ end
+
+ def release_name(release_type)
+ release_type.constantize.model_name.plural
end
end
diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb
index 5da02e0..c67e47a 100644
--- a/app/helpers/tags_helper.rb
+++ b/app/helpers/tags_helper.rb
@@ -17,6 +17,7 @@ module TagsHelper
disable_with: disabled_content,
},
form: {
+ id: "selected_releases_form",
data: {
releasable_ids: [],
},
diff --git a/app/jobs/generate_contracts_zip_job.rb b/app/jobs/generate_contracts_zip_job.rb
index 65bc376..43c5857 100644
--- a/app/jobs/generate_contracts_zip_job.rb
+++ b/app/jobs/generate_contracts_zip_job.rb
@@ -7,13 +7,14 @@ class GenerateContractsZipJob < ApplicationJob
@project = job.arguments.first
@download = job.arguments.second
@release_type = job.arguments.third
- @folder_name = "#{@project.name.parameterize}_#{get_release_name(@release_type).gsub('_', '-')}"
+ @release_ids = job.arguments.fourth
+ @search_query = job.arguments.fifth
+ @type_filter = job.arguments[5]
+ @folder_name = "#{@project.name.parameterize}_#{release_name.gsub('_', '-')}"
@download.update!(name: @folder_name, status: :pending)
end
- def perform(project, download, release_type, release_ids)
- releases = project.public_send(get_release_name(release_type)).where(id: release_ids)
-
+ def perform(project, download, release_type, release_ids, search_query, type_filter)
::ReleaseContractCollectionService.new(releases, @folder_name).build do |dir, files|
zipfile_name = "#{dir}/#{@folder_name}.zip"
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
@@ -31,7 +32,7 @@ class GenerateContractsZipJob < ApplicationJob
end
rescue StandardError => e
Rails.logger.error("Failed to generate download for project (##{project.id}) with release type #{release_type}\n" + e.message)
-
+
@download.failure!
ProjectsChannel.broadcast_download_generation_update(@download, I18n.t("contract_downloads.download.failure"))
end
@@ -61,7 +62,32 @@ class GenerateContractsZipJob < ApplicationJob
end
end
- def get_release_name(release_type)
- release_type.constantize.model_name.plural
+ def release_name
+ @release_type.constantize.model_name.plural
+ end
+
+ def all_releases
+ @project.public_send(release_name)
+ end
+
+ def releases
+ if @release_ids.any?
+ return all_releases.where(id: @release_ids)
+ end
+
+ results = all_releases
+ if all_releases.respond_to?(:complete, :incomplete)
+ results = case @type_filter
+ when 'complete'
+ all_releases.complete
+ when 'incomplete'
+ all_releases.incomplete
+ else
+ all_releases
+ end
+ end
+
+ results = results.search(@search_query) if @search_query.present?
+ results
end
end
diff --git a/app/models/appearance_release.rb b/app/models/appearance_release.rb
index c1888f6..5d913bd 100644
--- a/app/models/appearance_release.rb
+++ b/app/models/appearance_release.rb
@@ -164,6 +164,10 @@ class AppearanceRelease < ApplicationRecord
"#{project.name.parameterize}_#{contract_template.release_type}_#{(signed_at || created_at).strftime('%Y.%m.%d')}_#{release_number}_#{filename_suffix.parameterize}"
end
+ def complete?
+ person_photo.attached? && contract.attached?
+ end
+
private
# Validates the quality of the person photo
diff --git a/app/services/release_contract_collection_service.rb b/app/services/release_contract_collection_service.rb
index 898b42b..1b6b4a0 100644
--- a/app/services/release_contract_collection_service.rb
+++ b/app/services/release_contract_collection_service.rb
@@ -24,7 +24,7 @@ class ReleaseContractCollectionService
end
files = Dir.entries("#{dir}/").select { |f| !File.directory? f }
- raise StandardError.new "Contracts or Contract Templates not found." unless files.any?
+ # raise StandardError.new "Contracts or Contract Templates not found." unless files.any?
yield(dir, files)
}
end
diff --git a/app/views/appearance_releases/index.html.erb b/app/views/appearance_releases/index.html.erb
index 65b60ff..92f5ca5 100644
--- a/app/views/appearance_releases/index.html.erb
+++ b/app/views/appearance_releases/index.html.erb
@@ -1,5 +1,6 @@
+
/>
<% if policy(AppearanceRelease).new? %>
@@ -16,7 +17,7 @@
<% end %>
<% if @appearance_releases.any? && policy(AppearanceRelease).download_multiple? %>
- <%= link_to "Download All", [@project, :contract_downloads, release_type: @appearance_releases.name], method: :post, remote: true, class: "btn btn-light border mr-2 mb-2", data: { disable_with: "Please wait..." } %>
+ <%= button_to "Download", [@project, :contract_downloads, release_type: @appearance_releases.name], id: "download_releases", method: :post, remote: true, class: "btn btn-light border mr-2 mb-2", data: { disable_with: "Please wait..." } %>
<% end %>
diff --git a/app/views/appearance_releases/index.js.erb b/app/views/appearance_releases/index.js.erb
index ebc1c7c..e76d092 100644
--- a/app/views/appearance_releases/index.js.erb
+++ b/app/views/appearance_releases/index.js.erb
@@ -3,3 +3,5 @@ $("form input[type='search']").val("<%= params[:query] %>");
$("#type_filter_actions").html("<%= j render 'type_filter_actions' %>");
$("#appearance_releases_pagination").html("<%= j will_paginate(@appearance_releases) %>");
$('#type_filter_value').val("<%= params[:type_filter] %>");
+$("#selected_releases_form").attr('data-releasable-ids', JSON.stringify([]));
+$("#total_entries").val(<%= @appearance_releases.total_entries %>);
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 23b141d..562c785 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -1492,6 +1492,7 @@ CREATE TABLE public.settings (
--
CREATE SEQUENCE public.settings_id_seq
+ AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
@@ -1527,6 +1528,7 @@ CREATE TABLE public.taggings (
--
CREATE SEQUENCE public.taggings_id_seq
+ AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
@@ -1557,6 +1559,7 @@ CREATE TABLE public.tags (
--
CREATE SEQUENCE public.tags_id_seq
+ AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
diff --git a/spec/jobs/generate_contracts_zip_job_spec.rb b/spec/jobs/generate_contracts_zip_job_spec.rb
index 68bf43e..48e9cfc 100644
--- a/spec/jobs/generate_contracts_zip_job_spec.rb
+++ b/spec/jobs/generate_contracts_zip_job_spec.rb
@@ -19,7 +19,7 @@ describe GenerateContractsZipJob do
describe ".perform_later" do
it "enqueues a background job for generating zip file" do
expect {
- GenerateContractsZipJob.perform_later(project, download, "AppearanceRelease", project.appearance_releases.ids)
+ GenerateContractsZipJob.perform_later(project, download, "AppearanceRelease", project.appearance_releases.ids, '', '')
}.to have_enqueued_job
end
end
@@ -28,7 +28,7 @@ describe GenerateContractsZipJob do
shared_examples "generates ZIP containig CSV file with all releases data" do
it "generates ZIP containing CSV file with all releases data for all release types" do
lowercase_plural = subject.constantize.model_name.plural
- GenerateContractsZipJob.perform_now(project, download, subject, project.public_send(lowercase_plural).ids)
+ GenerateContractsZipJob.perform_now(project, download, subject, project.public_send(lowercase_plural).ids, '', '')
generated_zip = download.file.blob.download
csv_file_name = "#{project.name.parameterize}_#{lowercase_plural.gsub('_', '-')}.csv"
@@ -50,8 +50,111 @@ describe GenerateContractsZipJob do
end
end
+ shared_examples "generates ZIP containig CSV file with specific releases data" do
+ it "generates ZIP containing CSV file with all selected releases data for selected releases" do
+ lowercase_plural = subject.constantize.model_name.plural
+ all_releases = project.public_send(lowercase_plural)
+ included_releases = all_releases.where(id: all_releases.ids[0..1])
+ not_included_releases = all_releases.where.not(id: all_releases.ids[0..1])
+ GenerateContractsZipJob.perform_now(project, download, subject, included_releases.ids, '', '')
+
+ generated_zip = download.file.blob.download
+ csv_file_name = "#{project.name.parameterize}_#{lowercase_plural.gsub('_', '-')}.csv"
+ Zip::InputStream.open(StringIO.new(generated_zip)) do |io|
+ while entry = io.get_next_entry
+ next unless entry.name == csv_file_name
+
+ csv_file = entry.get_input_stream.read
+
+ release_class = Object.const_get subject
+ release_headers = release_class.csv_headers
+
+ release_headers.each do |header|
+ expect(csv_file).to match header
+ expect(csv_file).not_to match translation_missing
+ end
+
+ included_releases.each do |release|
+ expect(csv_file).to match release.person_first_name
+ end
+
+ not_included_releases.each do |release|
+ expect(csv_file).not_to match release.person_first_name
+ end
+ end
+ end
+ end
+
+ it "generates ZIP containing CSV file with all filtered releases data for filtered releases" do
+ lowercase_plural = subject.constantize.model_name.plural
+ GenerateContractsZipJob.perform_now(project, download, subject, [], '', 'complete')
+
+ complete_releases = project.public_send(lowercase_plural).complete
+ incomplete_releases = project.public_send(lowercase_plural).incomplete
+
+ generated_zip = download.file.blob.download
+ csv_file_name = "#{project.name.parameterize}_#{lowercase_plural.gsub('_', '-')}.csv"
+ Zip::InputStream.open(StringIO.new(generated_zip)) do |io|
+ while entry = io.get_next_entry
+ next unless entry.name == csv_file_name
+
+ csv_file = entry.get_input_stream.read
+
+ release_class = Object.const_get subject
+ release_headers = release_class.csv_headers
+
+ release_headers.each do |header|
+ expect(csv_file).to match header
+ expect(csv_file).not_to match translation_missing
+ end
+
+ complete_releases.each do |release|
+ expect(csv_file).to match release.person_first_name
+ end
+
+ incomplete_releases.each do |release|
+ expect(csv_file).not_to match release.person_first_name
+ end
+ end
+ end
+ end
+
+ it "generates ZIP containing CSV file with all search query matching releases" do
+ lowercase_plural = subject.constantize.model_name.plural
+ matched_releases = project.public_send(lowercase_plural).search('Brad')
+ not_matched_releases = project.public_send(lowercase_plural).where.not(id: matched_releases.ids)
+ GenerateContractsZipJob.perform_now(project, download, subject, [], 'Brad', '')
+
+ generated_zip = download.file.blob.download
+ csv_file_name = "#{project.name.parameterize}_#{lowercase_plural.gsub('_', '-')}.csv"
+ Zip::InputStream.open(StringIO.new(generated_zip)) do |io|
+ while entry = io.get_next_entry
+ next unless entry.name == csv_file_name
+
+ csv_file = entry.get_input_stream.read
+
+ release_class = Object.const_get subject
+ release_headers = release_class.csv_headers
+
+ release_headers.each do |header|
+ expect(csv_file).to match header
+ expect(csv_file).not_to match translation_missing
+ end
+
+ matched_releases.each do |release|
+ expect(csv_file).to match release.person_first_name
+ end
+
+ not_matched_releases.each do |release|
+ expect(csv_file).not_to match release.person_first_name
+ end
+ end
+ end
+ end
+ end
+
it "updates a download record and creates attachment for it" do
- GenerateContractsZipJob.perform_now(project, download, "AppearanceRelease", project.appearance_releases.ids)
+ GenerateContractsZipJob.perform_now(project, download, "AppearanceRelease", project.appearance_releases.ids, '', '')
expect(download.project).to eq project
expect(download.release_type).to eq "AppearanceRelease"
@@ -69,9 +172,12 @@ describe GenerateContractsZipJob do
context "generates ZIP for appearance releases" do
let(:release) { create(:appearance_release_with_contract_template, :native, project: project, person_name: "John Doe") }
+ let(:incomplete_release) { create(:appearance_release_with_contract_template, project: project, person_name: "Jane Doe") }
+ let(:complete_release) { create(:appearance_release_with_contract_template, :non_native, project: project, person_name: "Brad Doe") }
subject { 'AppearanceRelease' }
it_behaves_like "generates ZIP containig CSV file with all releases data"
+ it_behaves_like "generates ZIP containig CSV file with specific releases data"
end
context "generates ZIP for location releases" do
@@ -125,7 +231,7 @@ describe GenerateContractsZipJob do
end
it "updates status to 'failure' and sends user a notification" do
- GenerateContractsZipJob.perform_now(project, download, "AppearanceRelease", project.appearance_releases.ids)
+ GenerateContractsZipJob.perform_now(project, download, "AppearanceRelease", project.appearance_releases.ids, '', '')
expect(download.status).to eq "failure"
expect(ProjectsChannel).to have_received(:broadcast_download_generation_update).with(download, I18n.t("contract_downloads.download.failure"))