Skip to content

Commit 0d4b281

Browse files
authored
Allow sending set of provider-specific CSV files #420 (#430)
2 parents c384912 + 2abea01 commit 0d4b281

File tree

15 files changed

+167
-105
lines changed

15 files changed

+167
-105
lines changed

app/jobs/file_upload_job.rb

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
class FileUploadJob < ApplicationJob
22
# Consider removing concurrency limits due to SolidQueue blocking issues
33
# or use a more specific key to avoid blocking all jobs for a language
4-
limits_concurrency to: 3, key: ->(_language_id, content_id, _content_type) { "hard-limit" }
4+
limits_concurrency to: 3, key: ->(*args) { "hard-limit" }
55

66
retry_on AzureFileShares::Errors::ApiError, wait: :exponentially_longer, attempts: 3
77
retry_on Timeout::Error, wait: :exponentially_longer, attempts: 2
@@ -12,34 +12,34 @@ class FileUploadJob < ApplicationJob
1212
Rails.logger.error "Suggestion: Check provider names for invalid characters if Azure API errors"
1313
end
1414

15-
def perform(language_id, content_id, content_type, share = ENV["AZURE_STORAGE_SHARE_NAME"])
15+
def perform(language_id, file_id, provider_id = nil, share = ENV["AZURE_STORAGE_SHARE_NAME"])
1616
@language = Language.find(language_id)
17-
@processor = LanguageContentProcessor.new(language)
1817
@share = share
18+
@file_id = file_id.to_sym
19+
@processor = LanguageContentProcessor.new(language)
1920

20-
send_provider_content(content_id) if content_type == "provider"
21-
send_language_content(content_id.to_sym) if content_type == "file"
21+
send_provider_content(provider_id) if provider_id.present?
22+
send_language_content if provider_id.blank?
2223
end
2324

2425
private
2526

26-
attr_reader :language, :processor, :share
27+
attr_reader :language, :file_id, :share, :processor
2728

2829
def send_provider_content(provider_id)
2930
provider = language.providers.find(provider_id)
30-
return unless provider
31-
32-
processor.provider_files.each do |file|
33-
FileWorker.new(
34-
share:,
35-
name: file.name[provider],
36-
path: file.path,
37-
file: file.content[provider],
38-
).send
39-
end
31+
file = processor.provider_files[file_id]
32+
return unless provider && file
33+
34+
FileWorker.new(
35+
share:,
36+
name: file.name[provider],
37+
path: file.path,
38+
file: file.content[provider],
39+
).send
4040
end
4141

42-
def send_language_content(file_id)
42+
def send_language_content
4343
file = processor.language_files[file_id]
4444
return unless file
4545

app/services/csv_generator/base.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
class CsvGenerator::Base
2+
def initialize(source, **args)
3+
@source = source
4+
@args = args
5+
end
6+
27
def perform
38
CSV.generate(row_sep: "\n") do |csv|
49
csv << headers
@@ -7,4 +12,18 @@ def perform
712
end
813
end
914
end
15+
16+
private
17+
18+
attr_reader :source, :args
19+
20+
def topics_collection
21+
return source.topics if provider?
22+
23+
source.topics
24+
end
25+
26+
def language = language? ? source : args.fetch(:language)
27+
def language? = source.is_a?(Language)
28+
def provider? = source.is_a?(Provider)
1029
end

app/services/csv_generator/files.rb

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
class CsvGenerator::Files < CsvGenerator::Base
2-
def initialize(language, **args)
3-
@language = language
4-
@args = args
5-
end
6-
72
private
83

9-
attr_reader :language, :args
10-
114
def headers
125
%w[FileID TopicID FileName FileType FileSize]
136
end
147

158
def scope
16-
language.topics.active
9+
topics_collection.active
1710
.flat_map do |topic|
1811
topic.documents.map do |doc|
1912
[

app/services/csv_generator/tag_details.rb

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
class CsvGenerator::TagDetails < CsvGenerator::Base
2-
def initialize(language, **args)
3-
@language = language
4-
@args = args
5-
end
6-
72
private
83

9-
attr_reader :language, :args
10-
114
def headers
125
%w[TagID Tag]
136
end
147

158
def scope
16-
language.topics.active.includes(:tags)
9+
topics_collection.active.includes(:tags)
1710
.flat_map { |topic| topic.tags_on(language.code.to_sym) }
1811
.uniq
1912
.map do |tag|
Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
class CsvGenerator::TopicAuthors < CsvGenerator::Base
2-
def initialize(language, **args)
3-
@language = language
4-
@args = args
5-
end
6-
72
private
83

9-
attr_reader :language, :args
10-
114
def headers
125
%w[TopicID AuthorID]
136
end
147

158
def scope
16-
language.topics.active.map { |topic| [ topic.id, 0 ] }
9+
topics_collection.active.map { |topic| [ topic.id, 0 ] }
1710
end
1811
end

app/services/csv_generator/topic_tags.rb

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
class CsvGenerator::TopicTags < CsvGenerator::Base
2-
def initialize(language, **args)
3-
@language = language
4-
@args = args
5-
end
6-
72
private
83

9-
attr_reader :language, :args
10-
114
def headers
125
%w[TopicID TagID]
136
end
147

158
def scope
16-
language.topics.active.includes(:tags)
9+
topics_collection.active.includes(:tags)
1710
.flat_map do |topic|
1811
topic.tags_on(language.code.to_sym).map do |tag|
1912
[

app/services/csv_generator/topics.rb

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
class CsvGenerator::Topics < CsvGenerator::Base
2-
def initialize(language, **args)
3-
@language = language
4-
@args = args
5-
end
6-
72
private
83

9-
attr_reader :language, :args
10-
114
def headers
125
%w[TopicID TopicName TopicVolume TopicIssue TopicYear TopicMonth ContentProvider]
136
end
147

158
def scope
16-
language.topics.active.includes(:provider)
9+
topics_collection.active.includes(:provider)
1710
.map do |topic|
1811
[
1912
topic.id,

app/services/language_content_processor.rb

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,38 @@ def perform
1212
# this is needed to avoid loading all files into memory at once
1313
# Field 'name' is a lambda to allow dynamic naming based on the provider
1414
def provider_files
15-
[
16-
FileToUpload.new(
15+
{
16+
single_provider: FileToUpload.new(
1717
content: ->(provider) { XmlGenerator::SingleProvider.new(provider).perform },
1818
name: ->(provider) { "#{language.file_storage_prefix}#{provider.name.parameterize}.xml" },
1919
path: "#{language.file_storage_prefix}CMES-Pi/assets/XML",
2020
),
21-
]
21+
files: FileToUpload.new(
22+
content: ->(provider) { CsvGenerator::Files.new(provider).perform },
23+
name: ->(provider) { "#{language.file_storage_prefix}#{provider.name.parameterize}-file.csv" },
24+
path: "#{language.file_storage_prefix}CMES-v2/assets/csv",
25+
),
26+
topics: FileToUpload.new(
27+
content: ->(provider) { CsvGenerator::Topics.new(provider).perform },
28+
name: ->(provider) { "#{language.file_storage_prefix}#{provider.name.parameterize}-topic.csv" },
29+
path: "#{language.file_storage_prefix}CMES-v2/assets/csv",
30+
),
31+
tag_details: FileToUpload.new(
32+
content: ->(provider) { CsvGenerator::TagDetails.new(provider, language:).perform },
33+
name: ->(provider) { "#{language.file_storage_prefix}#{provider.name.parameterize}-tag.csv" },
34+
path: "#{language.file_storage_prefix}CMES-v2/assets/csv",
35+
),
36+
topic_tags: FileToUpload.new(
37+
content: ->(provider) { CsvGenerator::TopicTags.new(provider, language:).perform },
38+
name: ->(provider) { "#{language.file_storage_prefix}#{provider.name.parameterize}-topic-tag.csv" },
39+
path: "#{language.file_storage_prefix}CMES-v2/assets/csv",
40+
),
41+
topic_authors: FileToUpload.new(
42+
content: ->(provider) { CsvGenerator::TopicAuthors.new(provider).perform },
43+
name: ->(provider) { "#{language.file_storage_prefix}#{provider.name.parameterize}-topic-author.csv" },
44+
path: "#{language.file_storage_prefix}CMES-v2/assets/csv",
45+
),
46+
}
2247
end
2348

2449
# Field 'content' is a lambda to allow lazy evaluation
@@ -65,7 +90,7 @@ def language_files
6590
name: "#{language.file_storage_prefix}TopicTag.csv",
6691
path: "#{language.file_storage_prefix}CMES-v2/assets/csv",
6792
),
68-
topic_authors: FileToUpload.new(
93+
topic_authors: FileToUpload.new(
6994
content: ->(language) { CsvGenerator::TopicAuthors.new(language).perform },
7095
name: "#{language.file_storage_prefix}TopicAuthor.csv",
7196
path: "#{language.file_storage_prefix}CMES-v2/assets/csv",
@@ -79,11 +104,13 @@ def language_files
79104

80105
def process_language_content!
81106
language_files.keys.each do |file_id|
82-
FileUploadJob.perform_later(language.id, file_id.to_s, "file")
107+
FileUploadJob.perform_later(language.id, file_id.to_s)
83108
end
84109

85110
language.providers.distinct.find_each do |provider|
86-
FileUploadJob.perform_later(language.id, provider.id, "provider")
111+
provider_files.keys.each do |file_id|
112+
FileUploadJob.perform_later(language.id, file_id.to_s, provider.id)
113+
end
87114
end
88115
end
89116
end

spec/jobs/file_upload_job_spec.rb

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
file: file.content[language],
2020
)
2121

22-
described_class.perform_now(language.id, file_id.to_s, "file")
22+
described_class.perform_now(language.id, file_id.to_s)
2323
end
2424
end
2525
end
@@ -30,42 +30,31 @@
3030
before { create(:topic, :tagged, language:, provider:) }
3131

3232
it "processes specific file" do
33-
expect(FileWorker).to receive(:new).with(
34-
share: ENV["AZURE_STORAGE_SHARE_NAME"],
35-
name: "#{language.file_storage_prefix}test-provider.xml",
36-
path: "#{language.file_storage_prefix}CMES-Pi/assets/XML",
37-
file: XmlGenerator::SingleProvider.new(provider).perform,
38-
)
39-
40-
described_class.perform_now(language.id, provider.id, "provider")
41-
end
42-
43-
context "when provider name contains /" do
44-
let(:provider) { create(:provider, name: "Test/Provider") }
45-
46-
it "replaces / with - in the file name" do
33+
processor.provider_files.each do |file_id, file|
4734
expect(FileWorker).to receive(:new).with(
4835
share: ENV["AZURE_STORAGE_SHARE_NAME"],
49-
name: "#{language.file_storage_prefix}test-provider.xml",
50-
path: "#{language.file_storage_prefix}CMES-Pi/assets/XML",
51-
file: XmlGenerator::SingleProvider.new(provider).perform,
36+
name: file.name[provider],
37+
path: file.path,
38+
file: file.content[provider],
5239
)
5340

54-
described_class.perform_now(language.id, provider.id, "provider")
41+
described_class.perform_now(language.id, file_id.to_s, provider.id)
5542
end
43+
end
5644

57-
context "when provider name contains /" do
58-
let(:provider) { create(:provider, name: "WHO/Guidelines") }
45+
context "when provider name contains /" do
46+
let(:provider) { create(:provider, name: "Test/Provider") }
5947

60-
it "replaces / with - in the file name" do
48+
it "replaces / with - in the file name" do
49+
processor.provider_files.each do |file_id, file|
6150
expect(FileWorker).to receive(:new).with(
6251
share: ENV["AZURE_STORAGE_SHARE_NAME"],
63-
name: "#{language.file_storage_prefix}who-guidelines.xml",
64-
path: "#{language.file_storage_prefix}CMES-Pi/assets/XML",
65-
file: XmlGenerator::SingleProvider.new(provider).perform,
52+
name: file.name[provider],
53+
path: file.path,
54+
file: file.content[provider],
6655
)
6756

68-
described_class.perform_now(language.id, provider.id, "provider")
57+
described_class.perform_now(language.id, file_id.to_s, provider.id)
6958
end
7059
end
7160
end

spec/services/csv_generator/files_spec.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
require "rails_helper"
22

33
RSpec.describe CsvGenerator::Files do
4-
subject { described_class.new(language) }
4+
subject { described_class.new(source, **args) }
55

66
let(:language) { create(:language) }
7+
let(:source) { language }
8+
let(:args) { {} }
79
let(:header) { "FileID,TopicID,FileName,FileType,FileSize\n" }
810

911
it "generates empty csv" do
@@ -41,6 +43,15 @@
4143
expect(subject.perform).to eq(data)
4244
end
4345
end
46+
47+
context "when generated for provider" do
48+
let(:source) { topic.provider }
49+
let(:args) { { language: } }
50+
51+
it "generates csv with documents info" do
52+
expect(subject.perform).to eq(data)
53+
end
54+
end
4455
end
4556

4657
context "when topic exists but archived" do

0 commit comments

Comments
 (0)