almarklein / timetagger

Tag your time, get the insight
https://timetagger.app
GNU General Public License v3.0
1.17k stars 103 forks source link

RangeError: Maximum call stack size exceeded #493

Closed InteractionEngineer closed 4 months ago

InteractionEngineer commented 4 months ago

While manually adding entries from the past I pressed enter to confirm my "recording". I used the numpad enter key if that matters. One tag included an "ü" (german "ue") - which was not a problem before, when I confirmed my recordings with a click of the corresponding button. Immediately after pressing enter I saw in the background of the recording dialog that all previous entries were removed. Accessing the app with my credentials now leads to theses error messages:

Bildschirmfoto 2024-07-15 um 23 20 19 Bildschirmfoto 2024-07-15 um 23 19 47 Bildschirmfoto 2024-07-15 um 23 19 29

almarklein commented 4 months ago

Thanks for reporting this! I've not seen this before. Looking into it.

almarklein commented 4 months ago

Firstly, I don't think your records are actually gone.

Some context

In addition to storing the records themselves, the app maintains an in-memory data-structure (similar to a binary heap) that allows it to quickly query the summaries that are shown on the right. The _update_bins() method updates this data structure. This happens recursively. Somehow this recursion resulted in an infinite loop. I don't see how this could have happened.

Resolving

Could you try to refresh the page? The app should re-build this data structure from the records. It could be that the error persists, but then I have some more info on the cause.

For good measure, also try logging out and in again. That should clear the app's local cache.

InteractionEngineer commented 4 months ago

Hi there! Thanks for your quick reply. I was also positive about not have lost any data - great to hear this confirmed. Immediately after the error occurred I tried some of the common ways to solve it. I already:

None of these measures helped unfortunately.

almarklein commented 4 months ago

Thanks for the feedback! Ok, in that case there is probably something about the record times that causes the recursion.

It would help a lot if I could reproduce the error, so I can explore what's going on. I can than also attempt to fix it, and confirm whether the fix works or not.

If you're up for it, could you please try:

If you don't feel comfortable sending the full data, you could try the following:

InteractionEngineer commented 4 months ago

Tank you so much for your support - this sounds like a promising procedure. But there is one problem: The error blocks the whole UI, I can't access the menu to provide you an export.

Bildschirmfoto 2024-07-16 um 11 39 20

InteractionEngineer commented 4 months ago

What I forgot to mention: I run this as a Docker container on my home server. If you advise me, I can log into that container and export the wanted file directly.

almarklein commented 4 months ago

Can you run a Python script on your work machine? I could create a small script to extract the data via the web api.

InteractionEngineer commented 4 months ago

Of course, no problem!

almarklein commented 4 months ago

import datetime
import requests

# Replace with the url of your instance
base_url = "https://timetagger.app/api/v2/"

# Fill in the api token (from the account page)
api_token = ""

# Get an appropriate range
date1 = datetime.date(2024, 1, 1)
date2 = datetime.date(2024, 12, 31)

timestamp1 = int(time.mktime(date1.timetuple()))
timestamp2 = int(time.mktime(date2.timetuple()))
r = requests.get(base_url + f"records?timerange={timestamp1}-{timestamp2}", headers={"authtoken": api_token})

r.raise_for_status()
records = r.json()["records"]

print("start, stop")
for record in records:
    print(f"{record['t1']},{record['t2']}")
InteractionEngineer commented 4 months ago

Awesome! I quickly ran your script and got the following output:

start, stop 1612982100,1612985640 1620557100,1620557760 1622755080,1622761320 1622797200,1622799180 1624996620,1624998060 1625062920,1625063820

I choose 2020 to 2022 as range because the error occurred entering data from 2021.

almarklein commented 4 months ago

Is that all the data (that's just 6 records)?

InteractionEngineer commented 4 months ago

Indeed, I was just starting to import old data. New data would be from this year. I would prefer not to publish those. Are they important as well? The error surely results out of those 6 entries.

almarklein commented 4 months ago

I tried importing these 6, and it did not show the error, unfortunately. You could email me a bigger dataset, so that at least its not public. Or try to find a working subset with the steps:

InteractionEngineer commented 4 months ago

I tried recreating what you did but didn't succeed. I've emailed you a copy of the output for my complete dataset.

almarklein commented 4 months ago

Here's a script to see if there's records with odd dates.

import time
import datetime
import requests

# Replace with the url of your instance
base_url = "https://timetagger.app/api/v2/"

# Fill in the api token (from the account page)
api_token = ""

timestamp1 = 0
timestamp2 = 2218768380000
response = requests.get(base_url + f"records?timerange={timestamp1}-{timestamp2}", headers={"authtoken": api_token})
response.raise_for_status()

records = response.json()["records"]
records = sorted(records, key=lambda r: r["t1"])

def show_record(prefix, r):
    dt1 = datetime.datetime.fromtimestamp(r['t1'])
    dt2 = datetime.datetime.fromtimestamp(r['t2'])
    print(f"{prefix}: {r['key']}, from {dt1} to {dt2}")

print(f"{len(records)} records")
show_record("earliest", records[0])
show_record("latest", records[-1])

early_records = [r for r in records if datetime.datetime.fromtimestamp(r['t1']) < datetime.datetime(2000, 1, 1)]
for r in early_records:
    show_record("early", r)
future_records = [r for r in records if datetime.datetime.fromtimestamp(r['t2']) > datetime.datetime(2025, 1, 1)]
for r in future_records:
    show_record("future", r)

If there are records in a distant past or far future, these are likely the result of accidentally entering a wrong date in the the record dialog. An these may be causing the issue.

They can be fixed with something like this (I can help out write a more specific script):

def update_record(r):
    response = requests.put(base_url + "records", headers={"authtoken": api_token}, json=[r])
    response.raise_for_status()
    print(f"updated {r['key']}")

# .. update any records

# .. then send each record to server using  update_record()
InteractionEngineer commented 4 months ago

Hi Almar! My output looks like this:

21 records earliest: MAgwqbfM, from 2021-02-10 19:35:00 to 2021-02-10 20:34:00 latest: DBRxTXKV, from 2024-07-15 22:14:28 to 2024-07-15 22:35:00

Seemingly no entries in the too distant past or future.

almarklein commented 4 months ago

Oh interesting ...

Try replacing the request with this

response = requests.get(base_url + f"updates?since=0", headers={"authtoken": api_token})
response.raise_for_status()

records = response.json()["records"]
records = sorted(records, key=lambda r: r["t1"])
InteractionEngineer commented 4 months ago

There we go!

22 records earliest: xNOMAreB, from 1921-02-27 00:19:00 to 1921-02-27 00:43:00 latest: DBRxTXKV, from 2024-07-15 22:14:28 to 2024-07-15 22:35:00 early: xNOMAreB, from 1921-02-27 00:19:00 to 1921-02-27 00:43:00

almarklein commented 4 months ago

This should do the trick :) It moves the record back to now, so you can open the UI again, and then edit it properly :)

r = early_records[0]
r['t1'] = time.time()  # now
r['t2'] = r['t1'] + 1440  # keep duration
update_record(r)
InteractionEngineer commented 4 months ago

It work's again! Thank you so much! 🎉

almarklein commented 4 months ago

Thanks for your patience to work this out together!

I'm glad we were able to figure it out. I already made a few changes to prevent this from happening to other users. Will make a release shortly.

I'll also add some "diagnostic" commands to the CLI tool, based on the code samples we developed here.

almarklein commented 4 months ago

22