From f636d2a1773b2437fbf6928ee46ba5a2a9a56dee Mon Sep 17 00:00:00 2001 From: miteruzo Date: Sun, 12 Jul 2026 01:39:31 +0900 Subject: [PATCH] #399 --- backend/app/services/post_import_previewer.rb | 23 +++++++++++++++---- backend/app/services/post_import_runner.rb | 5 +++- frontend/src/pages/posts/PostImportPage.tsx | 8 ++++--- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/backend/app/services/post_import_previewer.rb b/backend/app/services/post_import_previewer.rb index 6facb97..6ab88c2 100644 --- a/backend/app/services/post_import_previewer.rb +++ b/backend/app/services/post_import_previewer.rb @@ -22,13 +22,20 @@ class PostImportPreviewer tag_sources = row[:tag_sources]&.stringify_keys || initial_tag_sources(attributes, provenance) tag_sources['manual'] = attributes['tags'].to_s if provenance['tags'] == 'manual' url = row[:url].presence || values[@url_column].to_s.strip - url_changed = row[:metadata_url].present? && row[:metadata_url] != url + url_for_metadata = normalised_url(url) || url + url_changed = row[:metadata_url].present? && row[:metadata_url] != url_for_metadata clear_automatic_values!(attributes, provenance, tag_sources) if url_changed provenance['url'] ||= row[:url].present? ? 'manual' : 'mapped' attributes['url'] = url fetch_warnings = Array(row[:fetch_warnings]).dup - should_fetch = fetch_metadata == true || fetch_metadata.to_i == row[:source_row].to_i - metadata, fetch_warnings = metadata_for(url, should_fetch, metadata_cache, fetch_warnings) + should_fetch = + case fetch_metadata + when true then true + when Integer then fetch_metadata == row[:source_row].to_i + when false, nil then false + else raise ArgumentError, 'メタデータ取得対象が不正です.' + end + metadata, fetch_warnings = metadata_for(url_for_metadata, should_fetch, metadata_cache, fetch_warnings) metadata.each do |field, value| if field == 'tags' && provenance[field] != 'manual' tag_sources['automatic'] = value.to_s @@ -56,9 +63,9 @@ class PostImportPreviewer attributes.delete('url') attributes['tags'] = merged_tags(tag_sources, provenance['tags']) { source_row: row[:source_row], url: normal_url || url, attributes:, provenance:, tag_sources:, - metadata_url: url, + metadata_url: url_for_metadata, fetch_warnings:, validation_warnings:, warnings: fetch_warnings + validation_warnings, - validation_errors: errors, errors:, + validation_errors: errors, status: errors.present? ? 'error' : ((fetch_warnings + validation_warnings).present? ? 'warning' : 'ready'), existing: normal_url.present? && Post.exists?(url: normal_url) } end @@ -70,6 +77,12 @@ class PostImportPreviewer cache[url] ||= fetch_metadata(url) end + def normalised_url value + post = Post.new(url: value) + post.valid? + post.url if post.errors[:url].empty? + end + private def mapped_value field, values diff --git a/backend/app/services/post_import_runner.rb b/backend/app/services/post_import_runner.rb index d21c3e5..5434269 100644 --- a/backend/app/services/post_import_runner.rb +++ b/backend/app/services/post_import_runner.rb @@ -30,7 +30,10 @@ class PostImportRunner attributes:, provenance: row['provenance'], tag_sources: row['tag_sources'] }], fetch_metadata: false).first - return { source_row: row['source_row'], status: 'failed', errors: preview[:errors] } if preview[:errors].present? + if preview[:validation_errors].present? + return { source_row: row['source_row'], status: 'failed', + errors: preview[:validation_errors] } + end return row.slice('source_row').merge(status: 'skipped') if @existing == 'skip' && preview[:existing] attributes['tags'] = preview[:attributes]['tags'] attributes['url'] = row['url'] diff --git a/frontend/src/pages/posts/PostImportPage.tsx b/frontend/src/pages/posts/PostImportPage.tsx index 4d810da..6b2b943 100644 --- a/frontend/src/pages/posts/PostImportPage.tsx +++ b/frontend/src/pages/posts/PostImportPage.tsx @@ -136,7 +136,7 @@ const PostImportPage: FC = ({ user }) => { importStatus: previous.importStatus, createdPostId: previous.createdPostId, importErrors: previous.importErrors, - validationErrors: row.validationErrors ?? row.errors ?? { } } : previous + validationErrors: row.validationErrors ?? { } } : previous }).concat (validated.filter (row => !current.some (_1 => _1.sourceRow === row.sourceRow))) const updateAttribute = async (index: number, field: string, value: string) => { @@ -198,12 +198,15 @@ const PostImportPage: FC = ({ user }) => { const updateExisting = async (value: 'skip' | 'error') => { setExisting (value) + const pendingRows = rows.map (row => row.importStatus === 'created' ? row : + { ...row, importStatus: 'pending' as const, importErrors: undefined }) + setRows (pendingRows) const generation = ++validationGeneration.current setLoading (true) try { const validated = await apiPost<{ rows: ImportRow[] }> ('/posts/import/validate', - { rows: rows.filter (row => row.importStatus !== 'created'), existing: value, changed_row: -1 }) + { rows: pendingRows.filter (row => row.importStatus !== 'created'), existing: value, changed_row: -1 }) if (generation === validationGeneration.current) setRows (current => mergeValidatedRows (current, validated.rows)) } @@ -234,7 +237,6 @@ const PostImportPage: FC = ({ user }) => { return resultRow ? { ...row, importStatus: resultRow.status, createdPostId: resultRow.post?.id, importErrors: resultRow.errors, - importStatus: resultRow.status, validationErrors: row.validationErrors } : row })) }