fix komoot sync stopping before all tours are imported (#917)
Three bugs caused partial imports: 1. Type filter halted pagination: if a page contained only tours of the excluded type (planned vs. recorded), syncTrailWithTours returned false and the loop stopped even though later pages might have matching tours. Fixed by tracking allAlreadySynced separately - only tours already in the DB count toward early termination, not type-filtered ones. 2. continue on error never incremented page: a fetch or sync error called continue on the outer for loop, so page stayed the same and hasNewTours was never updated, causing an infinite retry on the failing page. Changed to break so a failed page aborts the current sync cycle cleanly. 3. Pagination ignored TotalPages: the loop used a fragile stop heuristic instead of the TotalPages value returned by the Komoot API. fetchTours now returns TotalPages and the loop uses it as the upper bound, with the allAlreadySynced early-exit kept as an optimization for incremental syncs. Closes #570 Co-authored-by: slothful-vassal <89943360+slothful-vassal@users.noreply.github.com>
This commit is contained in:
@@ -68,25 +68,27 @@ func SyncKomoot(app core.App) error {
|
||||
app.Logger().Warn(warning)
|
||||
continue
|
||||
}
|
||||
hasNewTours := true
|
||||
page := 0
|
||||
for hasNewTours {
|
||||
tours, err := k.fetchTours(page)
|
||||
totalPages := 1
|
||||
for page := 0; page < totalPages; page++ {
|
||||
tours, tp, err := k.fetchTours(page)
|
||||
if err != nil {
|
||||
warning := fmt.Sprintf("error fetching tours from komoot: %v\n", err)
|
||||
warning := fmt.Sprintf("error fetching tours from komoot (page %d): %v\n", page, err)
|
||||
fmt.Print(warning)
|
||||
app.Logger().Warn(warning)
|
||||
continue
|
||||
break
|
||||
}
|
||||
totalPages = tp
|
||||
|
||||
hasNewTours, err = syncTrailWithTours(app, k, komootIntegration, userId, actorId, tours)
|
||||
allAlreadySynced, err := syncTrailWithTours(app, k, komootIntegration, userId, actorId, tours)
|
||||
if err != nil {
|
||||
warning := fmt.Sprintf("error syncing komoot tours with trails: %v\n", err)
|
||||
fmt.Print(warning)
|
||||
app.Logger().Warn(warning)
|
||||
continue
|
||||
break
|
||||
}
|
||||
if allAlreadySynced {
|
||||
break
|
||||
}
|
||||
page += 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,20 +158,18 @@ func (k *KomootApi) Login(email, password string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
func (k *KomootApi) fetchTours(page int) ([]KomootTour, error) {
|
||||
func (k *KomootApi) fetchTours(page int) ([]KomootTour, int, error) {
|
||||
currentUri := fmt.Sprintf("https://api.komoot.de/v007/users/%s/tours/?page=%d&sort_field=date&sort_direction=desc&limit=30", k.UserID, page)
|
||||
|
||||
body, err := sendRequest(currentUri, k.buildHeader())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var data KomootToursResponse
|
||||
json.Unmarshal(body, &data)
|
||||
|
||||
tours := data.Embedded.Tours
|
||||
|
||||
return tours, nil
|
||||
return data.Embedded.Tours, data.Page.TotalPages, nil
|
||||
}
|
||||
|
||||
func (k *KomootApi) fetchDetailedTour(tour KomootTour) (*DetailedKomootTour, error) {
|
||||
@@ -184,17 +184,25 @@ func (k *KomootApi) fetchDetailedTour(tour KomootTour) (*DetailedKomootTour, err
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// syncTrailWithTours imports tours not yet in the DB. Returns allAlreadySynced=true
|
||||
// when every tour on this page was already imported, so the caller can stop paginating
|
||||
// early during incremental syncs. Tours skipped due to type filters do NOT count as
|
||||
// synced - only tours already present in the DB do.
|
||||
func syncTrailWithTours(app core.App, k *KomootApi, i KomootIntegration, user string, actor string, tours []KomootTour) (bool, error) {
|
||||
hasNewTours := false
|
||||
allAlreadySynced := true
|
||||
for _, tour := range tours {
|
||||
trails, err := app.FindRecordsByFilter("trails", "external_id = {:id}", "", 1, 0, dbx.Params{"id": strconv.Itoa(int(tour.ID))})
|
||||
if err != nil {
|
||||
return hasNewTours, err
|
||||
return false, err
|
||||
}
|
||||
if len(trails) != 0 || (tour.Type == "tour_planned" && !i.Planned) || (tour.Type == "tour_recorded" && !i.Completed) {
|
||||
if len(trails) != 0 {
|
||||
continue
|
||||
}
|
||||
// Tour is not yet in the DB - we must keep paginating regardless of type filter
|
||||
allAlreadySynced = false
|
||||
if (tour.Type == "tour_planned" && !i.Planned) || (tour.Type == "tour_recorded" && !i.Completed) {
|
||||
continue
|
||||
}
|
||||
hasNewTours = true
|
||||
detailedTour, err := k.fetchDetailedTour(tour)
|
||||
if err != nil {
|
||||
app.Logger().Warn(fmt.Sprintf("Unable to fetch details for tour '%s': %v", tour.Name, err))
|
||||
@@ -217,7 +225,7 @@ func syncTrailWithTours(app core.App, k *KomootApi, i KomootIntegration, user st
|
||||
}
|
||||
|
||||
}
|
||||
return hasNewTours, nil
|
||||
return allAlreadySynced, nil
|
||||
}
|
||||
|
||||
func createTrailFromTour(app core.App, k *KomootApi, detailedTour *DetailedKomootTour, gpx *filesystem.File, user string, actor string, privacy string) (string, error) {
|
||||
|
||||
Reference in New Issue
Block a user