Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem importing from Google Takeout Records.json #279

Open
edbr-xyz opened this issue Sep 24, 2024 · 9 comments
Open

Problem importing from Google Takeout Records.json #279

edbr-xyz opened this issue Sep 24, 2024 · 9 comments

Comments

@edbr-xyz
Copy link

When I follow the steps to import a Records.json from Google Takeout, I get the following output:

/var/app # bundle exec rake import:big_file['public/imports/Records.json','<my-email>']
[dotenv] Set DATABASE_PORT and PHOTON_API_HOST
[dotenv] Loaded .env.development
D, [2024-09-24T14:07:26.464291 #150] DEBUG -- :   User Load (1.6ms)  SELECT "users".* FROM "users" WHERE "users"."email" = $1 LIMIT $2  [["email", "<my-email>"], ["LIMIT", 1]]
D, [2024-09-24T14:07:26.465388 #150] DEBUG -- :   ↳ app/services/tasks/imports/google_records.rb:9:in `initialize'
D, [2024-09-24T14:07:26.549560 #150] DEBUG -- :   TRANSACTION (0.2ms)  BEGIN
D, [2024-09-24T14:07:26.550475 #150] DEBUG -- :   ↳ app/services/tasks/imports/google_records.rb:26:in `create_import'
D, [2024-09-24T14:07:26.560035 #150] DEBUG -- :   Import Create (10.6ms)  INSERT INTO "imports" ("name", "user_id", "source", "created_at", "updated_at", "raw_points", "doubles", "processed", "raw_data", "points_count") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING "id"  [["name", "public/imports/Records.json"], ["user_id", 1], ["source", 2], ["created_at", "2024-09-24 14:07:26.548282"], ["updated_at", "2024-09-24 14:07:26.548282"], ["raw_points", 0], ["doubles", 0], ["processed", 0], ["raw_data", nil], ["points_count", 0]]
D, [2024-09-24T14:07:26.560962 #150] DEBUG -- :   ↳ app/services/tasks/imports/google_records.rb:26:in `create_import'
D, [2024-09-24T14:07:26.562775 #150] DEBUG -- :   TRANSACTION (1.4ms)  COMMIT
D, [2024-09-24T14:07:26.563269 #150] DEBUG -- :   ↳ app/services/tasks/imports/google_records.rb:26:in `create_import'
D, [2024-09-24T14:07:26.563671 #150] DEBUG -- : Importing public/imports/Records.json for <my-email>, file size is 742258184... This might take a while, have patience!
Killed
/var/app #

(my account email replaced with <my-email>)

The file that I am trying to import is quiet large, as seen in the above output.

I have tried upping the CPU and memory limits in docker-compose.yml. If I raise them enough, or remove the limits, the task will run, but will hang after a while, locking up the whole server, eventually spitting out the following:

^CE, [2024-09-24T14:04:12.363572 #156] ERROR -- : Failed enqueuing ImportGoogleTakeoutJob to Sidekiq(imports): Interrupt ()
I, [2024-09-24T14:04:12.363998 #156]  INFO -- : ↳ app/services/tasks/imports/google_records.rb:35:in `block in schedule_import_jobs'
rake aborted!
Interrupt: Interrupt
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client/ruby_connection/buffered_io.rb:213:in `wait_readable'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client/ruby_connection/buffered_io.rb:213:in `block in fill_buffer'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client/ruby_connection/buffered_io.rb:197:in `fill_buffer'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client/ruby_connection/buffered_io.rb:187:in `ensure_remaining'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client/ruby_connection/buffered_io.rb:152:in `getbyte'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client/ruby_connection/resp3.rb:113:in `parse'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client/ruby_connection/resp3.rb:50:in `load'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client/ruby_connection.rb:96:in `read'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client/connection_mixin.rb:52:in `block in call_pipelined'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client/connection_mixin.rb:50:in `call_pipelined'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client.rb:434:in `block (2 levels) in pipelined'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client/middlewares.rb:16:in `call'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client.rb:433:in `block in pipelined'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client.rb:699:in `ensure_connected'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client.rb:431:in `pipelined'
/var/app/vendor/bundle/ruby/3.3.0/gems/redis-client-0.22.2/lib/redis_client/decorator.rb:51:in `pipelined'
/var/app/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.2/lib/sidekiq/client.rb:228:in `block in raw_push'
/var/app/vendor/bundle/ruby/3.3.0/gems/connection_pool-2.4.1/lib/connection_pool.rb:110:in `block (2 levels) in with'
/var/app/vendor/bundle/ruby/3.3.0/gems/connection_pool-2.4.1/lib/connection_pool.rb:109:in `handle_interrupt'
/var/app/vendor/bundle/ruby/3.3.0/gems/connection_pool-2.4.1/lib/connection_pool.rb:109:in `block in with'
/var/app/vendor/bundle/ruby/3.3.0/gems/connection_pool-2.4.1/lib/connection_pool.rb:106:in `handle_interrupt'
/var/app/vendor/bundle/ruby/3.3.0/gems/connection_pool-2.4.1/lib/connection_pool.rb:106:in `with'
/var/app/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.2/lib/sidekiq/client.rb:225:in `raw_push'
/var/app/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.2/lib/sidekiq/client.rb:93:in `push'
/var/app/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.2/lib/sidekiq/job.rb:372:in `client_push'
/var/app/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.2/lib/sidekiq/job.rb:209:in `perform_async'
/var/app/vendor/bundle/ruby/3.3.0/gems/activejob-7.2.1/lib/active_job/queue_adapters/sidekiq_adapter.rb:25:in `enqueue'
/var/app/vendor/bundle/ruby/3.3.0/gems/activejob-7.2.1/lib/active_job/enqueuing.rb:133:in `raw_enqueue'
/var/app/vendor/bundle/ruby/3.3.0/gems/activejob-7.2.1/lib/active_job/enqueue_after_transaction_commit.rb:24:in `raw_enqueue'
/var/app/vendor/bundle/ruby/3.3.0/gems/activejob-7.2.1/lib/active_job/enqueuing.rb:118:in `block in enqueue'
/var/app/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.1/lib/active_support/callbacks.rb:121:in `block in run_callbacks'
/var/app/vendor/bundle/ruby/3.3.0/gems/activejob-7.2.1/lib/active_job/instrumentation.rb:40:in `block in instrument'
/var/app/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.1/lib/active_support/notifications.rb:210:in `block in instrument'
/var/app/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.1/lib/active_support/notifications/instrumenter.rb:58:in `instrument'
/var/app/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.1/lib/active_support/notifications.rb:210:in `instrument'
/var/app/vendor/bundle/ruby/3.3.0/gems/activejob-7.2.1/lib/active_job/instrumentation.rb:39:in `instrument'
/var/app/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.1/lib/active_record/railties/job_runtime.rb:18:in `instrument'
/var/app/vendor/bundle/ruby/3.3.0/gems/activejob-7.2.1/lib/active_job/instrumentation.rb:21:in `block (2 levels) in <module:Instrumentation>'
/var/app/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.1/lib/active_support/callbacks.rb:130:in `instance_exec'
/var/app/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.1/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/var/app/vendor/bundle/ruby/3.3.0/gems/activejob-7.2.1/lib/active_job/logging.rb:41:in `tag_logger'
/var/app/vendor/bundle/ruby/3.3.0/gems/activejob-7.2.1/lib/active_job/logging.rb:28:in `block (2 levels) in <module:Logging>'
/var/app/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.1/lib/active_support/callbacks.rb:130:in `instance_exec'
/var/app/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.1/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/var/app/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.1/lib/active_support/callbacks.rb:141:in `run_callbacks'
/var/app/vendor/bundle/ruby/3.3.0/gems/activejob-7.2.1/lib/active_job/enqueuing.rb:117:in `enqueue'
/var/app/vendor/bundle/ruby/3.3.0/gems/activejob-7.2.1/lib/active_job/enqueuing.rb:84:in `perform_later'
/var/app/app/services/tasks/imports/google_records.rb:35:in `block in schedule_import_jobs'
/var/app/app/services/tasks/imports/google_records.rb:34:in `each'
/var/app/app/services/tasks/imports/google_records.rb:34:in `schedule_import_jobs'
/var/app/app/services/tasks/imports/google_records.rb:19:in `call'
/var/app/lib/tasks/import.rake:9:in `block (2 levels) in <main>'
/var/app/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/exe/rake:27:in `<top (required)>'
/var/app/vendor/bundle/ruby/3.3.0/gems/bundler-2.5.9/lib/bundler/cli/exec.rb:58:in `load'
/var/app/vendor/bundle/ruby/3.3.0/gems/bundler-2.5.9/lib/bundler/cli/exec.rb:58:in `kernel_load'
/var/app/vendor/bundle/ruby/3.3.0/gems/bundler-2.5.9/lib/bundler/cli/exec.rb:23:in `run'
/var/app/vendor/bundle/ruby/3.3.0/gems/bundler-2.5.9/lib/bundler/cli.rb:451:in `exec'
/var/app/vendor/bundle/ruby/3.3.0/gems/bundler-2.5.9/lib/bundler/vendor/thor/lib/thor/command.rb:28:in `run'
/var/app/vendor/bundle/ruby/3.3.0/gems/bundler-2.5.9/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
/var/app/vendor/bundle/ruby/3.3.0/gems/bundler-2.5.9/lib/bundler/vendor/thor/lib/thor.rb:527:in `dispatch'
/var/app/vendor/bundle/ruby/3.3.0/gems/bundler-2.5.9/lib/bundler/cli.rb:34:in `dispatch'
/var/app/vendor/bundle/ruby/3.3.0/gems/bundler-2.5.9/lib/bundler/vendor/thor/lib/thor/base.rb:584:in `start'
/var/app/vendor/bundle/ruby/3.3.0/gems/bundler-2.5.9/lib/bundler/cli.rb:28:in `start'
/var/app/vendor/bundle/ruby/3.3.0/gems/bundler-2.5.9/exe/bundle:28:in `block in <top (required)>'
/var/app/vendor/bundle/ruby/3.3.0/gems/bundler-2.5.9/lib/bundler/friendly_errors.rb:117:in `with_friendly_errors'
/var/app/vendor/bundle/ruby/3.3.0/gems/bundler-2.5.9/exe/bundle:20:in `<top (required)>'
/usr/local/bundle/bin/bundle:25:in `load'
/usr/local/bundle/bin/bundle:25:in `<main>'
Tasks: TOP => import:big_file
(See full trace by running task with --trace)

I am running dawarich in openmediavault-7 docker-compose using the :latest dawarich version, on an AMD Ryzen 5 2400g with 8GB RAM.

@Danielson89
Copy link

I ended up exporting it directly from my phone using the Google Maps app. Google has really stuffed things up here. Once done from the phone the import worked as expected.

@applesoff
Copy link

i am having the same issue, but i cannot figure out how Danielson89 fixed it the way they did. any tips?

@Freika
Copy link
Owner

Freika commented Oct 5, 2024

I'd suggest splitting the Records.json file into smaller chunks: https://dawarich.app/docs/FAQ#why-my-attempt-to-import-recordsjson-fails

@Svagtlys
Copy link

@applesoff In case you haven't gotten your data yet, these steps are what I used now that Google has swapped from online to local by default storage of timeline data:

https://support.google.com/maps/thread/280205453/how-do-i-download-my-timeline-history?hl=en

Instructions:
try from android device settings > location > location services > timeline > export timeline data

@mcfrojd
Copy link

mcfrojd commented Nov 14, 2024

My Records.json is 1.56 GB and 70 million lines! (data from 2010 till today)
How big chunks can dawarich handle?
And any tips on best way to split upp the file?

@Freika
Copy link
Owner

Freika commented Nov 15, 2024

@mcfrojd splitting to files sized up to 100-150MB should work

@AngryJKirk
Copy link

I come back to this project once in a month trying to import my data (around 900mb) and it always fails.

I can't believe the technology is not there yet to read and put in a database less than a gigabyte of data. 😭

@AngryJKirk
Copy link

AngryJKirk commented Nov 17, 2024

I ended up solving this by a python script:

import json
from datetime import datetime


def generate_sql(file_path, output_path, import_id, user_id):
    now = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]

    with open(file_path, 'r') as json_file, open(output_path, 'w') as sql_file:
        data = json.load(json_file)
        locations = data.get('locations', [])

        for location in locations:
            parsed = parse_json(location)
            sql = (
                f"INSERT INTO public.points (latitude, longitude, timestamp, raw_data, topic, tracker_id, import_id, user_id, created_at, updated_at) "
                f"VALUES ({parsed['latitude']}, {parsed['longitude']}, {parsed['timestamp']}, "
                f"'{parsed['raw_data']}', 'Google Maps Timeline Export', 'google-maps-timeline-export', "
                f"{import_id}, {user_id}, '{now}', '{now}');\n"
            )
            sql_file.write(sql)

def parse_json(entry):
    timestamp_str = entry.get('timestamp') or entry.get('timestampMs', '')


    if 'T' in timestamp_str:
        timestamp = int(datetime.fromisoformat(timestamp_str.replace('Z', '+00:00')).timestamp())
    else:
        timestamp = int(timestamp_str) // 1000 if timestamp_str else 0

    return {
        "latitude": entry.get('latitudeE7', 0) / 10 ** 7,
        "longitude": entry.get('longitudeE7', 0) / 10 ** 7,
        "timestamp": timestamp,
        "altitude": entry.get('altitude', 'NULL'),
        "velocity": entry.get('velocity', 'NULL'),
        "raw_data": json.dumps(entry).replace("'", "''") 
    }


input_json_path = 'Records.json'
output_sql_path = 'output.sql'
import_id = 1
user_id = 1

generate_sql(input_json_path, output_sql_path, import_id, user_id)

Steps:

  1. create import
  2. wait until it fails
  3. put the user id and import id according to the database (it will be 1 and 1 if you run it on a fresh install)
  4. put your Records.json next to the script
  5. run the script
  6. modify docker-compose.yml to expose the port of the database, e.g.
    ports:
      - "127.0.0.1:5432:5432"
  1. get the output.sql and just run it against the database (you will need to modify docker-compose.yml to expose the port for the database, e.g. )
  2. wait around 10-15 minutes (it took 11 minutes for 2 million rows)

After that I see all my points in the app. I checked the code and that seems to be the only thing to be done, @Freika please correct me if I am wrong, I am seeing Ruby for the first time in my life

@Nathagamelle
Copy link

Nathagamelle commented Nov 20, 2024

            sql = (
                f"INSERT INTO public.points (latitude, longitude, timestamp, raw_data, topic, tracker_id, import_id, user_id, created_at, updated_at) "
                f"VALUES ({parsed['latitude']}, {parsed['longitude']}, {parsed['timestamp']}, "
                f"'{parsed['raw_data']}', '**Google Maps Timeline Export', 'google-maps-timeline-export**', "
                f"{import_id}, {user_id}, '{now}', '{now}');\n"
            )

tempted to do it, but is there any reason to keep the long topic and tracker_id ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants