Fix import detection

This commit is contained in:
Eugene Burmakin 2025-08-23 16:07:15 +02:00
parent 001d294885
commit b049c11542
13 changed files with 61 additions and 34 deletions

View file

@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
# [UNRELEASED] -
## Changed
- If user already have import with the same name, it will be appended with timestamp during the import process.
## Fixed
- Some types of imports were not being detected correctly and were failing to import.
# [0.30.10] - 2025-08-22
## Added

View file

@ -102,27 +102,25 @@ class ImportsController < ApplicationController
blob = ActiveStorage::Blob.find_signed(signed_id)
import = current_user.imports.build(name: blob.filename.to_s)
import_name = generate_unique_import_name(blob.filename.to_s)
import = current_user.imports.build(name: import_name)
import.file.attach(blob)
import.source = detect_import_source(import.file) if import.source.blank?
import.save!
import
end
def detect_import_source(file_attachment)
temp_file_path = Imports::SecureFileDownloader.new(file_attachment).download_to_temp_file
def generate_unique_import_name(original_name)
return original_name unless current_user.imports.exists?(name: original_name)
Imports::SourceDetector.new_from_file_header(temp_file_path).detect_source
rescue StandardError => e
Rails.logger.warn "Failed to auto-detect import source for #{file_attachment.filename}: #{e.message}"
nil
ensure
# Cleanup temp file
if temp_file_path && File.exist?(temp_file_path)
File.unlink(temp_file_path)
end
# Extract filename and extension
basename = File.basename(original_name, File.extname(original_name))
extension = File.extname(original_name)
# Add current datetime
timestamp = Time.current.strftime('%Y%m%d_%H%M%S')
"#{basename}_#{timestamp}#{extension}"
end
def validate_points_limit

View file

@ -1,7 +1,10 @@
# frozen_string_literal: true
class Settings::BackgroundJobsController < ApplicationController
before_action :authenticate_self_hosted!
before_action :authenticate_self_hosted!, unless: lambda {
%w[start_immich_import start_photoprism_import].include?(params[:job_name])
}
before_action :authenticate_admin!, unless: lambda {
%w[start_immich_import start_photoprism_import].include?(params[:job_name])
}

View file

@ -21,7 +21,7 @@ class Import < ApplicationRecord
google_semantic_history: 0, owntracks: 1, google_records: 2,
google_phone_takeout: 3, gpx: 4, immich_api: 5, geojson: 6, photoprism_api: 7,
user_data_archive: 8
}
}, allow_nil: true
def process!
if user_data_archive?

View file

@ -16,7 +16,12 @@ class Imports::Create
temp_file_path = Imports::SecureFileDownloader.new(import.file).download_to_temp_file
source = import.source.presence || detect_source_from_file(temp_file_path)
source = if import.source.nil? || should_detect_source?
detect_source_from_file(temp_file_path)
else
import.source
end
importer(source).new(import, user.id, temp_file_path).call
schedule_stats_creating(user.id)
@ -90,8 +95,14 @@ class Imports::Create
).call
end
def should_detect_source?
# Don't override API-based sources that can't be reliably detected
!%w[immich_api photoprism_api].include?(import.source)
end
def detect_source_from_file(temp_file_path)
detector = Imports::SourceDetector.new_from_file_header(temp_file_path)
detector.detect_source!
end

View file

@ -0,0 +1,5 @@
class RemoveDefaultFromImportsSource < ActiveRecord::Migration[8.0]
def change
change_column_default :imports, :source, from: 0, to: nil
end
end

7
db/schema.rb generated
View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_07_28_191359) do
ActiveRecord::Schema[8.0].define(version: 2025_08_23_125940) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
enable_extension "postgis"
@ -99,7 +99,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_07_28_191359) do
create_table "imports", force: :cascade do |t|
t.string "name", null: false
t.bigint "user_id", null: false
t.integer "source", default: 0
t.integer "source"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "raw_points", default: 0
@ -230,7 +230,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_07_28_191359) do
t.datetime "end_at", null: false
t.bigint "user_id", null: false
t.geometry "original_path", limit: {srid: 0, type: "line_string"}, null: false
t.integer "distance"
t.decimal "distance", precision: 8, scale: 2
t.float "avg_speed"
t.integer "duration"
t.integer "elevation_gain"
@ -274,6 +274,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_07_28_191359) do
t.string "last_sign_in_ip"
t.integer "status", default: 0
t.datetime "active_until"
t.integer "points_count", default: 0, null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end

View file

@ -4,7 +4,7 @@ FactoryBot.define do
factory :import do
user
sequence(:name) { |n| "owntracks_export_#{n}.json" }
source { Import.sources[:owntracks] }
# source { Import.sources[:owntracks] }
trait :with_points do
after(:create) do |import|

View file

@ -1,5 +1,3 @@
// This file contains 3 doubles
{
"semanticSegments": [
{

View file

@ -14,7 +14,7 @@ RSpec.describe GoogleMaps::PhoneTakeoutImporter do
context 'when file content is an object' do
# This file contains 3 duplicates
let(:file_path) { Rails.root.join('spec/fixtures/files/google/phone-takeout.json') }
let(:file_path) { Rails.root.join('spec/fixtures/files/google/phone-takeout_w_3_duplicates.json') }
let(:file) { Rack::Test::UploadedFile.new(file_path, 'application/json') }
let(:import) { create(:import, user:, name: 'phone_takeout.json', file:) }

View file

@ -63,10 +63,10 @@ RSpec.describe Imports::Create do
context 'when source is google_phone_takeout' do
let(:import) { create(:import, source: 'google_phone_takeout') }
let(:file_path) { Rails.root.join('spec/fixtures/files/google/phone-takeout.json') }
let(:file_path) { Rails.root.join('spec/fixtures/files/google/phone-takeout_w_3_duplicates.json') }
before do
import.file.attach(io: File.open(file_path), filename: 'phone-takeout.json',
import.file.attach(io: File.open(file_path), filename: 'phone-takeout_w_3_duplicates.json',
content_type: 'application/json')
end
@ -193,6 +193,7 @@ RSpec.describe Imports::Create do
it 'calls the Photos::Importer' do
expect(Photos::Importer).to \
receive(:new).with(import, user.id, kind_of(String)).and_return(double(call: true))
service.call
end
end

View file

@ -24,7 +24,7 @@ RSpec.describe Imports::SourceDetector do
end
context 'with Google Phone Takeout format' do
let(:file_content) { file_fixture('google/phone-takeout.json').read }
let(:file_content) { file_fixture('google/phone-takeout_w_3_duplicates.json').read }
it 'detects google_phone_takeout format' do
expect(detector.detect_source).to eq(:google_phone_takeout)