WIP: export/import for account notes

This commit is contained in:
Emelia Smith 2024-10-11 22:02:11 +02:00
parent e15befebbd
commit 3ee385ed2e
No known key found for this signature in database
11 changed files with 95 additions and 3 deletions

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
module Settings
module Exports
class AccountNotesController < BaseController
include Settings::ExportControllerConcern
def index
send_export_file
end
private
def export_data
@export.to_account_notes_csv
end
end
end
end

View file

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

View file

@ -55,6 +55,14 @@ class Export
end end
end end
def to_account_notes_csv
CSV.generate(headers: ['Account address', 'Account note'], write_headers: true, encoding: Encoding::UTF_8) do |csv|
account.account_notes.includes(:target_account).reorder(id: :desc).each do |account_note|
csv << [acct(account_note.target_account), account_note.comment]
end
end
end
private private
def to_csv(accounts) def to_csv(accounts)

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'],
account_notes: ['Account address', 'Account note'],
}.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
@ -32,6 +33,7 @@ class Form::Import
'#domain' => 'domain', '#domain' => 'domain',
'#uri' => 'uri', '#uri' => 'uri',
'List name' => 'list_name', 'List name' => 'list_name',
'Account note' => 'comment',
}.freeze }.freeze
class EmptyFileError < StandardError; end class EmptyFileError < StandardError; end
@ -55,6 +57,8 @@ class Form::Import
:bookmarks :bookmarks
elsif file_name_matches?('lists') elsif file_name_matches?('lists')
:lists :lists
elsif csv_headers_match?('Account note') || file_name_matches?('account_notes')
:account_notes
end end
end end
@ -102,6 +106,8 @@ class Form::Import
['#uri'] ['#uri']
when :lists when :lists
['List name', 'Account address'] ['List name', 'Account address']
when :account_notes
['Account address', 'Account note']
end end
end end
@ -118,7 +124,7 @@ class Form::Import
field.strip.gsub(/\A@/, '') field.strip.gsub(/\A@/, '')
when '#domain' when '#domain'
field&.strip&.downcase field&.strip&.downcase
when '#uri', 'List name' when '#uri', 'List name', 'Account note'
field.strip field.strip
else else
field field

View file

@ -14,6 +14,11 @@ class ExportSummary
prefix: true prefix: true
) )
delegate(
:account_notes,
to: :account
)
def initialize(account) def initialize(account)
@account = account @account = account
@counts = populate_counts @counts = populate_counts
@ -47,6 +52,10 @@ class ExportSummary
counts[:muting].value counts[:muting].value
end end
def total_account_notes
counts[:account_notes].value
end
def total_statuses def total_statuses
account.statuses_count account.statuses_count
end end
@ -64,6 +73,7 @@ class ExportSummary
domain_blocks: account_domain_blocks.async_count, domain_blocks: account_domain_blocks.async_count,
owned_lists: account_owned_lists.async_count, owned_lists: account_owned_lists.async_count,
muting: account_muting.async_count, muting: account_muting.async_count,
account_notes: account_notes.async_count,
storage: account_media_attachments.async_sum(:file_file_size), storage: account_media_attachments.async_sum(:file_file_size),
} }
end end

View file

@ -7,7 +7,7 @@ class BulkImportRowService
@type = row.bulk_import.type.to_sym @type = row.bulk_import.type.to_sym
case @type case @type
when :following, :blocking, :muting, :lists when :following, :blocking, :muting, :lists, :account_notes
target_acct = @data['acct'] target_acct = @data['acct']
target_domain = domain(target_acct) target_domain = domain(target_acct)
@target_account = stoplight_wrapper(target_domain).run { ResolveAccountService.new.call(target_acct, { check_delivery_availability: true }) } @target_account = stoplight_wrapper(target_domain).run { ResolveAccountService.new.call(target_acct, { check_delivery_availability: true }) }
@ -39,6 +39,10 @@ class BulkImportRowService
FollowService.new.call(@account, @target_account) unless @account.id == @target_account.id FollowService.new.call(@account, @target_account) unless @account.id == @target_account.id
list.accounts << @target_account list.accounts << @target_account
when :account_notes
@note = AccountNote.find_or_initialize_by(account: @account, target_account: @target_account)
@note.comment = @data['comment']
@note.save! if @note.changed?
end end
true true

View file

@ -18,6 +18,8 @@ class BulkImportService < BaseService
import_bookmarks! import_bookmarks!
when :lists when :lists
import_lists! import_lists!
when :account_notes
import_account_notes!
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 +184,32 @@ class BulkImportService < BaseService
[row.id] [row.id]
end end
end end
def import_account_notes!
rows_by_acct = extract_rows_by_acct
if @import.overwrite?
@account.account_notes.reorder(nil).find_each do |account_note|
row = rows_by_acct.delete(account_note.target_account.acct)
if row.nil?
account_note.destroy!
else
row.destroy
@import.processed_items += 1
@import.imported_items += 1
@note = AccountNote.find_or_initialize_by(account: @account, target_account: row.data['acct'])
@note.comment = row.data['comment']
@note.save! if @note.changed?
end
end
# Save pending infos due to `overwrite?` handling
@import.save!
end
Import::RowWorker.push_bulk(rows) do |row|
[row.id]
end
end
end end

View file

@ -12,6 +12,10 @@
%th= t('accounts.posts_tab_heading') %th= t('accounts.posts_tab_heading')
%td= number_with_delimiter @export_summary.total_statuses %td= number_with_delimiter @export_summary.total_statuses
%td %td
%tr
%th= t('exports.account_notes')
%td= number_with_delimiter @export_summary.total_account_notes
%td= table_link_to 'download', t('exports.csv'), settings_exports_account_notes_path(format: :csv)
%tr %tr
%th= t('admin.accounts.follows') %th= t('admin.accounts.follows')
%td= number_with_delimiter @export_summary.total_follows %td= number_with_delimiter @export_summary.total_follows

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(muting blocking domain_blocking), other: %i(account_notes) },
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

@ -1367,6 +1367,9 @@ en:
overwrite: Overwrite overwrite: Overwrite
overwrite_long: Replace current records with the new ones overwrite_long: Replace current records with the new ones
overwrite_preambles: overwrite_preambles:
account_notes_html:
one: You are about to import <strong>%{count} account note</strong> from <strong>%{filename}</strong>. Any existing account note on this account will be overwritten.
other: You are about to import <strong>%{count} account notes</strong> from <strong>%{filename}</strong>. Any existing account notes on these accounts will be overwritten.
blocking_html: blocking_html:
one: You are about to <strong>replace your block list</strong> with up to <strong>%{count} account</strong> from <strong>%{filename}</strong>. one: You are about to <strong>replace your block list</strong> with up to <strong>%{count} account</strong> from <strong>%{filename}</strong>.
other: You are about to <strong>replace your block list</strong> with up to <strong>%{count} accounts</strong> from <strong>%{filename}</strong>. other: You are about to <strong>replace your block list</strong> with up to <strong>%{count} accounts</strong> from <strong>%{filename}</strong>.
@ -1386,6 +1389,9 @@ en:
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>.
preambles: preambles:
account_notes_html:
one: You are about to import <strong>%{count} account note</strong> from <strong>%{filename}</strong>.
other: You are about to import <strong>%{count} account notes</strong> from <strong>%{filename}</strong>.
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>.
other: You are about to <strong>block</strong> up to <strong>%{count} accounts</strong> from <strong>%{filename}</strong>. other: You are about to <strong>block</strong> up to <strong>%{count} accounts</strong> from <strong>%{filename}</strong>.
@ -1415,6 +1421,7 @@ en:
success: Your data was successfully uploaded and will be processed in due time success: Your data was successfully uploaded and will be processed in due time
time_started: Started at time_started: Started at
titles: titles:
account_notes: Importing account notes
blocking: Importing blocked accounts blocking: Importing blocked accounts
bookmarks: Importing bookmarks bookmarks: Importing bookmarks
domain_blocking: Importing blocked domains domain_blocking: Importing blocked domains
@ -1425,6 +1432,7 @@ en:
type_groups: type_groups:
constructive: Follows & Bookmarks constructive: Follows & Bookmarks
destructive: Blocks & mutes destructive: Blocks & mutes
other: Other
types: types:
blocking: Blocking list blocking: Blocking list
bookmarks: Bookmarks bookmarks: Bookmarks
@ -1432,6 +1440,7 @@ en:
following: Following list following: Following list
lists: Lists lists: Lists
muting: Muting list muting: Muting list
account_notes: Account notes
upload: Upload upload: Upload
invites: invites:
delete: Deactivate delete: Deactivate

View file

@ -26,6 +26,7 @@ namespace :settings do
resources :follows, only: :index, controller: :following_accounts resources :follows, only: :index, controller: :following_accounts
resources :blocks, only: :index, controller: :blocked_accounts resources :blocks, only: :index, controller: :blocked_accounts
resources :mutes, only: :index, controller: :muted_accounts resources :mutes, only: :index, controller: :muted_accounts
resources :account_notes, only: :index, controller: :account_notes
resources :lists, only: :index resources :lists, only: :index
resources :domain_blocks, only: :index, controller: :blocked_domains resources :domain_blocks, only: :index, controller: :blocked_domains
resources :bookmarks, only: :index resources :bookmarks, only: :index