FEATURE: Add category tracking state to user archive export (#10557)

FEATURE: Add category tracking state to user archive export (#10557)

Tackling a simple table for the first actual new file in the user archive export.

diff --git a/app/jobs/regular/export_user_archive.rb b/app/jobs/regular/export_user_archive.rb
index 8fc08d9..4990c43 100644
--- a/app/jobs/regular/export_user_archive.rb
+++ b/app/jobs/regular/export_user_archive.rb
@@ -13,11 +13,13 @@ module Jobs
     COMPONENTS ||= %w(
       user_archive
       user_archive_profile
+      category_preferences
     )
 
     HEADER_ATTRS_FOR ||= HashWithIndifferentAccess.new(
       user_archive: ['topic_title', 'categories', 'is_pm', 'post', 'like_count', 'reply_count', 'url', 'created_at'],
       user_archive_profile: ['location', 'website', 'bio', 'views'],
+      category_preferences: ['category_id', 'category_names', 'notification_level', 'dismiss_new_timestamp'],
     )
 
     def execute(args)
@@ -124,6 +126,22 @@ module Jobs
       end
     end
 
+    def category_preferences_export
+      return enum_for(:category_preferences_export) unless block_given?
+
+      CategoryUser
+        .where(user_id: @current_user.id)
+        .select(:category_id, :notification_level, :last_seen_at)
+        .each do |cu|
+        yield [
+          cu.category_id,
+          piped_category_name(cu.category.id),
+          NotificationLevels.all[cu.notification_level],
+          cu.last_seen_at
+        ]
+      end
+    end
+
     def get_header(entity)
       if entity == 'user_list'
         header_array = HEADER_ATTRS_FOR['user_list'] + HEADER_ATTRS_FOR['user_stats'] + HEADER_ATTRS_FOR['user_profile']
diff --git a/spec/jobs/export_user_archive_spec.rb b/spec/jobs/export_user_archive_spec.rb
index e71f074..35a51ea 100644
--- a/spec/jobs/export_user_archive_spec.rb
+++ b/spec/jobs/export_user_archive_spec.rb
@@ -15,10 +15,15 @@ describe Jobs::ExportUserArchive do
   let(:component) { raise 'component not set' }
 
   def make_component_csv
-    CSV.generate do |csv|
+    data_rows = []
+    csv_out = CSV.generate do |csv|
       csv << job.get_header(component)
-      job.public_send(:"#{component}_export") { |d| csv << d }
+      job.public_send(:"#{component}_export") do |row|
+        csv << row
+        data_rows << Jobs::ExportUserArchive::HEADER_ATTRS_FOR[component].zip(row.map(&:to_s)).to_h.with_indifferent_access
+      end
     end
+    [data_rows, csv_out]
   end
 
   context '#execute' do
@@ -137,11 +142,63 @@ describe Jobs::ExportUserArchive do
     end
 
     it 'properly includes the profile fields' do
-      csv_out = make_component_csv
+      _, csv_out = make_component_csv
 
       expect(csv_out).to match('doe.example.com')
       expect(csv_out).to match("Doe\n\nHere")
     end
   end
 
+  context 'category_preferences' do
+    let(:component) { 'category_preferences' }
+
+    let(:category) { Fabricate(:category_with_definition) }
+    let(:subcategory) { Fabricate(:category_with_definition, parent_category_id: category.id) }
+    let(:subsubcategory) { Fabricate(:category_with_definition, parent_category_id: subcategory.id) }
+    let(:announcements) { Fabricate(:category_with_definition) }
+
+    let(:reset_at) { DateTime.parse('2017-03-01 12:00') }
+
+    before do
+      SiteSetting.max_category_nesting = 3
+
+      # TopicsController#reset-new?category_id=&include_subcategories=true
+      category_ids = [subcategory.id, subsubcategory.id]
+      category_ids.each do |category_id|
+        user
+          .category_users
+          .where(category_id: category_id)
+          .first_or_initialize
+          .update!(last_seen_at: reset_at)
+        #TopicTrackingState.publish_dismiss_new(user.id, category_id)
+      end
+
+      # Set Watching First Post on announcements, Tracking on subcategory, nothing on subsubcategory
+      CategoryUser.set_notification_level_for_category(user, NotificationLevels.all[:watching_first_post], announcements.id)
+      CategoryUser.set_notification_level_for_category(user, NotificationLevels.all[:tracking], subcategory.id)
+    end
+
+    it 'correctly exports the CategoryUser table' do
+      data, csv_out = make_component_csv
+
+      expect(data.find { |r| r['category_id'] == category.id }).to be_nil
+
+      data.sort { |a, b| a['category_id'] <=> b['category_id'] }
+
+      expect(data[0][:category_id]).to eq(subcategory.id.to_s)
+      expect(data[0][:notification_level].to_s).to eq('tracking')
+      expect(DateTime.parse(data[0][:dismiss_new_timestamp])).to eq(reset_at)
+
+      expect(data[1][:category_id]).to eq(subsubcategory.id.to_s)
+      expect(data[1][:category_names]).to eq("#{category.name}|#{subcategory.name}|#{subsubcategory.name}")
+      expect(data[1][:notification_level]).to eq('') # empty string, not 'normal'
+      expect(DateTime.parse(data[1][:dismiss_new_timestamp])).to eq(reset_at)
+
+      expect(data[2][:category_id]).to eq(announcements.id.to_s)
+      expect(data[2][:category_names]).to eq(announcements.name)
+      expect(data[2][:notification_level]).to eq('watching_first_post')
+      expect(data[2][:dismiss_new_timestamp]).to eq('')
+    end
+  end
+
 end

GitHub sha: c5dc729e

This commit appears in #10557 which was approved by eviltrout. It was merged by riking.