Skip to content

Commit 5ed5062

Browse files
authored
XML: group by published_at with year->month nesting; dedupe providers… (#407)
2 parents a3cbdd9 + 542e049 commit 5ed5062

File tree

4 files changed

+118
-87
lines changed

4 files changed

+118
-87
lines changed

app/helpers/topics_helper.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,28 @@ def card_preview_media(file)
1212
private
1313

1414
def render_image(file)
15-
image_tag(url_for(file), class: "img-fluid w-100")
15+
image_tag(rails_blob_path(file, disposition: "inline"), class: "img-fluid w-100")
1616
end
1717

1818
def render_pdf(file)
1919
content_tag(:div, class: "embed-responsive embed-responsive-item embed-responsive-16by9 w-100") do
20-
content_tag(:object, data: url_for(file), type: "application/pdf", width: "100%", height: "400px") do
21-
content_tag(:iframe, "", src: url_for(file), width: "100%", height: "100%", style: "border: none;") do
22-
content_tag(:p, "Your browser does not support PDF viewing. #{link_to('Download the PDF', url_for(file))}")
20+
content_tag(:object, data: rails_blob_path(file, disposition: "inline"), type: "application/pdf", width: "100%", height: "400px") do
21+
content_tag(:iframe, "", src: rails_blob_path(file, disposition: "inline"), width: "100%", height: "100%", style: "border: none;") do
22+
content_tag(:p, "Your browser does not support PDF viewing. #{link_to('Download the PDF', rails_blob_path(file))}")
2323
end
2424
end
2525
end
2626
end
2727

2828
def render_video(file)
29-
video_tag(url_for(file), style: "width: 100%")
29+
video_tag(rails_blob_path(file, disposition: "inline"), style: "width: 100%")
3030
end
3131

3232
def render_audio(file)
33-
audio_tag(url_for(file), controls: true, style: "width: 100%")
33+
audio_tag(rails_blob_path(file, disposition: "inline"), controls: true, style: "width: 100%")
3434
end
3535

3636
def render_download_link(file)
37-
link_to file.filename, url_for(file)
37+
link_to file.filename, rails_blob_path(file)
3838
end
3939
end

app/services/xml_generator/single_provider.rb

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,36 @@ def provider_xml(provider)
1616
Ox::Element.new("content_provider").tap do |xml|
1717
xml[:name] = provider.name
1818

19-
grouped_topics(provider).each do |(year, month), topics|
19+
grouped_topics(provider).sort_by { |year, _| -year.to_i }.each do |(year, months)|
2020
xml << Ox::Element.new("topic_year").tap do |year_element|
2121
year_element[:year] = year.to_s
22-
year_element << Ox::Element.new("topic_month").tap do |month_element|
23-
month_element[:month] = month
24-
topics.each do |topic|
25-
month_element << Ox::Element.new("title").tap do |title_element|
26-
title_element[:name] = topic.title
27-
title_element << Ox::Element.new("topic_id").tap { |id| id << topic.id.to_s }
28-
title_element << Ox::Element.new("topic_files").tap do |files|
29-
files[:files] = "Files"
30-
topic.documents.each_with_index do |document, index|
31-
next if document.content_type == "video/mp4"
32-
files << Ox::Element.new("file_name_#{index + 1}").tap do |file_name|
33-
file_name << document.filename.to_s
34-
file_name[:file_size] = document.byte_size
22+
months.sort_by { |month_label, _| month_label }.each do |(month, topics)|
23+
year_element << Ox::Element.new("topic_month").tap do |month_element|
24+
month_element[:month] = month
25+
topics.each do |topic|
26+
month_element << Ox::Element.new("title").tap do |title_element|
27+
title_element[:name] = topic.title
28+
title_element << Ox::Element.new("topic_id").tap { |id| id << topic.id.to_s }
29+
title_element << Ox::Element.new("counter").tap { |c| c << "0" }
30+
title_element << Ox::Element.new("topic_volume").tap { |e| e << topic.published_at.year.to_s }
31+
title_element << Ox::Element.new("topic_issue").tap { |e| e << topic.published_at.month.to_s }
32+
title_element << Ox::Element.new("topic_files").tap do |files|
33+
files[:files] = "Files"
34+
topic.documents.each_with_index do |document, index|
35+
next if document.content_type == "video/mp4"
36+
files << Ox::Element.new("file_name_#{index + 1}").tap do |file_name|
37+
file_name << document.filename.to_s
38+
file_name[:file_size] = document.byte_size
39+
end
3540
end
3641
end
37-
end
38-
title_element << Ox::Element.new("topic_tags").tap do |tags|
39-
names = topic.taggings.map { |tg| tg.tag&.name }.compact.uniq
40-
tags << names.join(", ")
42+
title_element << Ox::Element.new("topic_author").tap do |author|
43+
author << Ox::Element.new("topic_author_1").tap { |a| a << " " }
44+
end
45+
title_element << Ox::Element.new("topic_tags").tap do |tags|
46+
names = topic.taggings.map { |tg| tg.tag&.name }.compact.uniq
47+
tags << names.join(", ")
48+
end
4149
end
4250
end
4351
end
@@ -48,16 +56,19 @@ def provider_xml(provider)
4856
end
4957

5058
def grouped_topics(prov)
51-
topic_scope(prov).group_by { |topic| [ topic.published_at.year, topic.published_at.strftime("%m_%B") ] }
59+
# { year => { "MM_Month" => [topics] } }
60+
topic_scope(prov)
61+
.group_by { |topic| topic.published_at.year }
62+
.transform_values { |topics_in_year| topics_in_year.group_by { |t| t.published_at.strftime("%m_%B") } }
5263
end
5364

5465
def topic_scope(prov)
5566
scope = prov.topics
5667
scope = scope.where("published_at > ?", 1.month.ago) if args.fetch(:recent, false)
5768
scope
5869
.select(:id, :title, :published_at, :language_id, :provider_id)
59-
.includes(:language, taggings: :tag) # eager-load language and taggings->tag
60-
.with_attached_documents # eager-load Active Storage attachments + blobs
70+
.includes(:language, taggings: :tag)
71+
.with_attached_documents
6172
.order(published_at: :desc)
6273
end
6374
end

spec/services/xml_generator/all_providers_spec.rb

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,36 +19,47 @@
1919
end
2020

2121
it "generates the xml" do
22-
expect(subject.perform).to eq(
23-
<<~TEXT
24-
<?xml version="1.0"?>
25-
<cmes>
26-
<content_provider name="#{provider1.name}">
27-
<topic_year year="#{topic1.published_at.year}">
28-
<topic_month month="#{topic1.published_at.strftime("%m_%B")}">
29-
<title name="#{topic1.title}">
30-
<topic_id>#{topic1.id}</topic_id>
31-
<topic_files files="Files"/>
32-
<topic_tags>#{topic1.current_tags_list.join(", ")}</topic_tags>
33-
</title>
34-
</topic_month>
35-
</topic_year>
36-
</content_provider>
37-
<content_provider name="#{provider2.name}">
38-
<topic_year year="#{topic2.published_at.year}">
39-
<topic_month month="#{topic2.published_at.strftime("%m_%B")}">
40-
<title name="#{topic2.title}">
41-
<topic_id>#{topic2.id}</topic_id>
42-
<topic_files files="Files">
43-
<file_name_1 file_size="494323">test_image.png</file_name_1>
44-
</topic_files>
45-
<topic_tags>#{topic2.current_tags_list.join(", ")}</topic_tags>
46-
</title>
47-
</topic_month>
48-
</topic_year>
49-
</content_provider>
50-
</cmes>
51-
TEXT
52-
)
22+
xml = subject.perform
23+
doc = Nokogiri::XML(xml)
24+
25+
[ provider1, provider2 ].each do |prov|
26+
pnode = doc.at_xpath("//cmes/content_provider[@name='#{prov.name}']")
27+
expect(pnode).to be_present
28+
end
29+
30+
[ [ provider1, topic1 ], [ provider2, topic2 ] ].each do |prov, topic|
31+
pnode = doc.at_xpath("//cmes/content_provider[@name='#{prov.name}']")
32+
year = topic.published_at.year
33+
ynode = pnode.at_xpath("./topic_year[@year='#{year}']")
34+
expect(ynode).to be_present
35+
36+
month_label = topic.published_at.strftime("%m_%B")
37+
mnode = ynode.at_xpath("./topic_month[@month='#{month_label}']")
38+
expect(mnode).to be_present
39+
40+
tnode = mnode.at_xpath("./title[@name='#{topic.title}']")
41+
expect(tnode).to be_present
42+
43+
expect(tnode.at_xpath("./topic_id").text).to eq(topic.id.to_s)
44+
expect(tnode.at_xpath("./counter").text).to eq("0")
45+
expect(tnode.at_xpath("./topic_volume").text).to eq(topic.published_at.year.to_s)
46+
expect(tnode.at_xpath("./topic_issue").text).to eq(topic.published_at.month.to_s)
47+
48+
files = tnode.at_xpath("./topic_files[@files='Files']")
49+
expect(files).to be_present
50+
if topic.documents.attached? && topic.documents.reject { |d| d.content_type == "video/mp4" }.any?
51+
first_file = files.element_children.first
52+
expect(first_file).to be_present
53+
expect(first_file.text).to be_present
54+
expect(first_file["file_size"]).to be_present
55+
else
56+
expect(files.element_children).to be_empty
57+
end
58+
59+
author1 = tnode.at_xpath("./topic_author/topic_author_1")
60+
expect(author1.text).to eq(" ")
61+
62+
expect(tnode.at_xpath("./topic_tags").text).to eq(topic.current_tags_list.join(", "))
63+
end
5364
end
5465
end

spec/services/xml_generator/single_provider_spec.rb

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@
66
let(:provider) { create(:provider) }
77

88
it "generates the xml" do
9-
expect(subject.perform).to eq(
10-
<<~TEXT
11-
<?xml version="1.0"?>
12-
<cmes>
13-
<content_provider name="#{provider.name}"/>
14-
</cmes>
15-
TEXT
16-
)
9+
xml = subject.perform
10+
doc = Nokogiri::XML(xml)
11+
provider_nodes = doc.xpath("//cmes/content_provider[@name='#{provider.name}']")
12+
expect(provider_nodes.size).to eq(1)
13+
# No topics → self-closing provider node (no children)
14+
expect(provider_nodes.first.element_children).to be_empty
1715
end
1816

1917
context "with topics" do
@@ -35,26 +33,37 @@
3533
end
3634

3735
it "generates the xml" do
38-
expect(subject.perform).to eq(
39-
<<~TEXT
40-
<?xml version="1.0"?>
41-
<cmes>
42-
<content_provider name="#{provider.name}">
43-
<topic_year year="#{topic.published_at.year}">
44-
<topic_month month="#{topic.published_at.strftime("%m_%B")}">
45-
<title name="#{topic.title}">
46-
<topic_id>#{topic.id}</topic_id>
47-
<topic_files files="Files">
48-
<file_name_1 file_size="494323">test_image.png</file_name_1>
49-
</topic_files>
50-
<topic_tags>#{topic.current_tags_list.join(", ")}</topic_tags>
51-
</title>
52-
</topic_month>
53-
</topic_year>
54-
</content_provider>
55-
</cmes>
56-
TEXT
57-
)
36+
xml = subject.perform
37+
doc = Nokogiri::XML(xml)
38+
39+
provider_node = doc.at_xpath("//cmes/content_provider[@name='#{provider.name}']")
40+
expect(provider_node).to be_present
41+
42+
year_node = provider_node.at_xpath("./topic_year[@year='#{topic.published_at.year}']")
43+
expect(year_node).to be_present
44+
45+
month_label = topic.published_at.strftime("%m_%B")
46+
month_node = year_node.at_xpath("./topic_month[@month='#{month_label}']")
47+
expect(month_node).to be_present
48+
49+
title_node = month_node.at_xpath("./title[@name='#{topic.title}']")
50+
expect(title_node).to be_present
51+
52+
expect(title_node.at_xpath("./topic_id").text).to eq(topic.id.to_s)
53+
expect(title_node.at_xpath("./counter").text).to eq("0")
54+
expect(title_node.at_xpath("./topic_volume").text).to eq(topic.published_at.year.to_s)
55+
expect(title_node.at_xpath("./topic_issue").text).to eq(topic.published_at.month.to_s)
56+
57+
files_node = title_node.at_xpath("./topic_files[@files='Files']")
58+
expect(files_node).to be_present
59+
file1 = files_node.at_xpath("./file_name_1")
60+
expect(file1.text).to eq("test_image.png")
61+
expect(file1["file_size"]).to be_present
62+
63+
author_node = title_node.at_xpath("./topic_author/topic_author_1")
64+
expect(author_node.text).to eq(" ")
65+
66+
expect(title_node.at_xpath("./topic_tags").text).to eq(topic.current_tags_list.join(", "))
5867
end
5968
end
6069
end

0 commit comments

Comments
 (0)