Working on import

This commit is contained in:
Evan Summers 2025-08-07 18:27:07 +02:00
parent 61c4aab3e4
commit dbdf20b373
7 changed files with 56 additions and 15 deletions

View File

@ -13,6 +13,7 @@ class Settings::ImportsController < Settings::BaseController
domain_blocking: 'blocked_domains_failures.csv', domain_blocking: 'blocked_domains_failures.csv',
bookmarks: 'bookmarks_failures.csv', bookmarks: 'bookmarks_failures.csv',
lists: 'lists_failures.csv', lists: 'lists_failures.csv',
filters: 'filters_failures.csv',
}.freeze }.freeze
TYPE_TO_HEADERS_MAP = { TYPE_TO_HEADERS_MAP = {
@ -22,6 +23,7 @@ class Settings::ImportsController < Settings::BaseController
domain_blocking: false, domain_blocking: false,
bookmarks: false, bookmarks: false,
lists: false, lists: false,
filters: false,
}.freeze }.freeze
RECENT_IMPORTS_LIMIT = 10 RECENT_IMPORTS_LIMIT = 10

View File

@ -34,6 +34,7 @@ class BulkImport < ApplicationRecord
domain_blocking: 3, domain_blocking: 3,
bookmarks: 4, bookmarks: 4,
lists: 5, lists: 5,
filters: 6,
} }
enum :state, { enum :state, {

View File

@ -19,6 +19,7 @@ class Form::Import
domain_blocking: ['#domain'], domain_blocking: ['#domain'],
bookmarks: ['#uri'], bookmarks: ['#uri'],
lists: ['List name', 'Account address'], lists: ['List name', 'Account address'],
filters: ['Title', 'Context', 'Keywords', 'Action', 'Expire after'],
}.freeze }.freeze
KNOWN_FIRST_HEADERS = EXPECTED_HEADERS_BY_TYPE.values.map(&:first).uniq.freeze KNOWN_FIRST_HEADERS = EXPECTED_HEADERS_BY_TYPE.values.map(&:first).uniq.freeze
@ -55,6 +56,8 @@ class Form::Import
:bookmarks :bookmarks
elsif file_name_matches?('lists') elsif file_name_matches?('lists')
:lists :lists
elsif file_name_matches?('filters') || csv_headers_match?('Keywords')
:filters
end end
end end
@ -102,6 +105,8 @@ class Form::Import
['#uri'] ['#uri']
when :lists when :lists
['List name', 'Account address'] ['List name', 'Account address']
when :filters
['Title', 'Context', 'Keywords', 'Action', 'Expire after']
end end
end end
@ -109,19 +114,30 @@ class Form::Import
return @csv_data if defined?(@csv_data) return @csv_data if defined?(@csv_data)
csv_converter = lambda do |field, field_info| csv_converter = lambda do |field, field_info|
case field_info.header if :type == :filters
when 'Show boosts', 'Notify on new posts', 'Hide notifications' case field_info.header
ActiveModel::Type::Boolean.new.cast(field&.downcase) when 'Context', 'Keywords'
when 'Languages' field&.split(',')&.map(&:strip)&.presence
field&.split(',')&.map(&:strip)&.presence when 'Expire after'
when 'Account address' field.blank? ? nil : Time.zone.parse(field)
field.strip.gsub(/\A@/, '') else
when '#domain' field
field&.strip&.downcase end
when '#uri', 'List name'
field.strip
else else
field case field_info.header
when 'Show boosts', 'Notify on new posts', 'Hide notifications'
ActiveModel::Type::Boolean.new.cast(field&.downcase)
when 'Languages'
field&.split(',')&.map(&:strip)&.presence
when 'Account address'
field.strip.gsub(/\A@/, '')
when '#domain'
field&.strip&.downcase
when '#uri', 'List name'
field.strip
else
field
end
end end
end end

View File

@ -18,6 +18,10 @@ class BulkImportService < BaseService
import_bookmarks! import_bookmarks!
when :lists when :lists
import_lists! import_lists!
when :filters
import_filters!
else
raise NotImplementedError, "Unknown import type: #{@import.type}"
end end
@import.update!(state: :finished, finished_at: Time.now.utc) if @import.processed_items == @import.total_items @import.update!(state: :finished, finished_at: Time.now.utc) if @import.processed_items == @import.total_items
@ -182,4 +186,14 @@ class BulkImportService < BaseService
[row.id] [row.id]
end end
end end
def import_filters!
rows = @import.rows.to_a
@account.custom_filters.destroy_all if @import.overwrite?
Import::RowWorker.push_bulk(rows) do |row|
[row.id]
end
end
end end

View File

@ -5,7 +5,7 @@
.field-group .field-group
= f.input :type, = f.input :type,
as: :grouped_select, as: :grouped_select,
collection: { constructive: %i(following bookmarks lists), destructive: %i(muting blocking domain_blocking) }, collection: { constructive: %i(following bookmarks lists), destructive: %i(filters muting blocking domain_blocking) },
group_label_method: ->(group) { I18n.t("imports.type_groups.#{group.first}") }, group_label_method: ->(group) { I18n.t("imports.type_groups.#{group.first}") },
group_method: :last, group_method: :last,
hint: t('imports.preface'), hint: t('imports.preface'),

View File

@ -1517,6 +1517,9 @@ en:
muting_html: muting_html:
one: You are about to <strong>replace your list of muted account</strong> with up to <strong>%{count} account</strong> from <strong>%{filename}</strong>. one: You are about to <strong>replace your list of muted account</strong> with up to <strong>%{count} account</strong> from <strong>%{filename}</strong>.
other: You are about to <strong>replace your list of muted accounts</strong> with up to <strong>%{count} accounts</strong> from <strong>%{filename}</strong>. other: You are about to <strong>replace your list of muted accounts</strong> with up to <strong>%{count} accounts</strong> from <strong>%{filename}</strong>.
filters_html:
one: You are about to <strong>replace your filters</strong> with up to <strong>%{count} filter</strong> from <strong>%{filename}</strong>.
other: You are about to <strong>replace your filters</strong> with up to <strong>%{count} filters</strong> from <strong>%{filename}</strong>.
preambles: preambles:
blocking_html: blocking_html:
one: You are about to <strong>block</strong> up to <strong>%{count} account</strong> from <strong>%{filename}</strong>. one: You are about to <strong>block</strong> up to <strong>%{count} account</strong> from <strong>%{filename}</strong>.
@ -1536,6 +1539,9 @@ en:
muting_html: muting_html:
one: You are about to <strong>mute</strong> up to <strong>%{count} account</strong> from <strong>%{filename}</strong>. one: You are about to <strong>mute</strong> up to <strong>%{count} account</strong> from <strong>%{filename}</strong>.
other: You are about to <strong>mute</strong> up to <strong>%{count} accounts</strong> from <strong>%{filename}</strong>. other: You are about to <strong>mute</strong> up to <strong>%{count} accounts</strong> from <strong>%{filename}</strong>.
filters_html:
one: You are about to <strong>add</strong> up to <strong>%{count} filter</strong> from <strong>%{filename}</strong>.
other: You are about to <strong>add</strong> up to <strong>%{count} filters</strong> from <strong>%{filename}</strong>.
preface: You can import data that you have exported from another server, such as a list of the people you are following or blocking. preface: You can import data that you have exported from another server, such as a list of the people you are following or blocking.
recent_imports: Recent imports recent_imports: Recent imports
states: states:
@ -1564,6 +1570,7 @@ en:
following: Following list following: Following list
lists: Lists lists: Lists
muting: Muting list muting: Muting list
filters: Filters
upload: Upload upload: Upload
invites: invites:
delete: Deactivate delete: Deactivate

View File

@ -112,8 +112,9 @@ RSpec.describe Export do
it 'returns a csv of custom filters' do it 'returns a csv of custom filters' do
expect(export) expect(export)
.to contain_exactly( .to contain_exactly(
include(/statuses/), contain_exactly('Title', 'Context', 'Keywords', 'Action', 'Expire after'),
include(/statuses/) include('discourse', 'home,notifications', 'discourse', 'warn', be_blank),
include(be_present)
) )
end end
end end