Skip to content

Commit c05d81d

Browse files
authored
Merge pull request #3 from vangent/save-progress
add --start_row to handle partial success
2 parents d2fabf3 + 730afa3 commit c05d81d

File tree

3 files changed

+76
-39
lines changed

3 files changed

+76
-39
lines changed

cmd/update.go

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func init() {
3939
var accessToken string
4040
var origFile string
4141
var updatedFile string
42+
var startRow int
4243
var dryRun bool
4344

4445
updateCmd := &cobra.Command{
@@ -54,7 +55,7 @@ See "stravacli help download" for info about the data columns.
5455
`,
5556
Args: cobra.NoArgs,
5657
RunE: func(_ *cobra.Command, _ []string) error {
57-
return doUpdate(accessToken, origFile, updatedFile, dryRun)
58+
return checkPartialSuccess(doUpdate(accessToken, origFile, updatedFile, startRow, dryRun))
5859
},
5960
}
6061
updateCmd.Flags().StringVarP(&accessToken, "access_token", "t", "", "Strava access token; use the auth command to get one")
@@ -63,6 +64,7 @@ See "stravacli help download" for info about the data columns.
6364
updateCmd.MarkFlagRequired("orig")
6465
updateCmd.Flags().StringVar(&updatedFile, "updated", "", ".csv with modifications")
6566
updateCmd.MarkFlagRequired("updated")
67+
updateCmd.Flags().IntVar(&startRow, "start_row", 1, "skip rows in the input up to this row (row 0 is the header row)")
6668
updateCmd.Flags().BoolVar(&dryRun, "dryrun", false, "do a dry run: print out proposed changes")
6769
rootCmd.AddCommand(updateCmd)
6870
}
@@ -80,10 +82,10 @@ func loadUpdatableActivitiesFromCSV(filename string) ([]*updatableActivity, erro
8082
return activities, nil
8183
}
8284

83-
func doUpdate(accessToken, origFile, updatedFile string, dryRun bool) error {
85+
func doUpdate(accessToken, origFile, updatedFile string, startRow int, dryRun bool) (int, error) {
8486
activities, err := loadUpdatableActivitiesFromCSV(origFile)
8587
if err != nil {
86-
return err
88+
return 0, err
8789
}
8890
orig := map[int64]*updatableActivity{}
8991
for _, a := range activities {
@@ -92,37 +94,41 @@ func doUpdate(accessToken, origFile, updatedFile string, dryRun bool) error {
9294

9395
activities, err = loadUpdatableActivitiesFromCSV(updatedFile)
9496
if err != nil {
95-
return err
97+
return 0, err
9698
}
9799

98100
if len(activities) != len(orig) {
99-
return fmt.Errorf("%q has %d activities, but %q has %d; for update, they should be the same", origFile, len(orig), updatedFile, len(activities))
101+
return 0, fmt.Errorf("%q has %d activities, but %q has %d; for update, they should be the same", origFile, len(orig), updatedFile, len(activities))
100102
}
101103
ctx := context.WithValue(context.Background(), strava.ContextAccessToken, accessToken)
102104
apiSvc := strava.NewAPIClient(strava.NewConfiguration()).ActivitiesApi
103105

104-
fmt.Printf("Found %d activities....\n", len(activities))
105-
nUpdates := 0
106+
fmt.Printf("Found %d activities%s....\n", len(activities), startRowMessage(len(activities), startRow))
107+
n := 0
106108
for i, a := range activities {
109+
row := i + 1 // row 0 is the header row
110+
if row < startRow {
111+
continue
112+
}
107113
prev := orig[a.ID]
108114
if prev == nil {
109-
return fmt.Errorf("activity ID %d from %q not found in %q", a.ID, updatedFile, origFile)
115+
return row, fmt.Errorf("activity ID %d from %q not found in %q", a.ID, updatedFile, origFile)
110116
}
111117
if *prev == *a {
112118
log.Printf("no change for ID %d", a.ID)
113119
continue
114120
}
115121
if err := updateOne(ctx, apiSvc, a, prev, dryRun); err != nil {
116-
return fmt.Errorf("failed to update activity %v on line %d: %v", a, i+1, err)
122+
return row, fmt.Errorf("failed to update activity %v: %v", a, err)
117123
}
118-
nUpdates++
124+
n++
119125
}
120126
if dryRun {
121-
fmt.Printf("Found %d activities to be updated.\n", nUpdates)
127+
fmt.Printf("Found %d activities to be updated.\n", n)
122128
} else {
123-
fmt.Printf("Updated %d activities.\n", nUpdates)
129+
fmt.Printf("Updated %d activities.\n", n)
124130
}
125-
return nil
131+
return 0, nil
126132
}
127133

128134
func updateOne(ctx context.Context, apiSvc *strava.ActivitiesApiService, a, prev *updatableActivity, dryRun bool) error {
@@ -156,3 +162,27 @@ func updateOne(ctx context.Context, apiSvc *strava.ActivitiesApiService, a, prev
156162
fmt.Printf(" --> https://www.strava.com/activities/%d\n", detailedActivity.Id)
157163
return nil
158164
}
165+
166+
func startRowMessage(n, startRow int) string {
167+
if startRow <= 1 {
168+
return ""
169+
}
170+
n = n - startRow + 1
171+
if n < 0 {
172+
n = 0
173+
}
174+
return fmt.Sprintf(", %d after starting on row %d", n, startRow)
175+
}
176+
177+
func checkPartialSuccess(row int, err error) error {
178+
if err == nil {
179+
// Success! Nothing to do.
180+
return nil
181+
}
182+
if row <= 1 {
183+
// Failed, but failed on or before the first row; nothing to do.
184+
return err
185+
}
186+
// Tell the user how to restart.
187+
return fmt.Errorf("%v\n\nSome rows were successfully processed, but there was an error on row %d (note: row 0 is the header row). Fix the error and rerun with '--start_row=%d' to retry, or rerun with '--start_row=%d' to skip over the bad row", err, row, row, row+1)
188+
}

cmd/upload.go

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import (
4040
func init() {
4141
var accessToken string
4242
var inFile string
43+
var startRow int
4344
var dryRun bool
4445

4546
uploadCmd := &cobra.Command{
@@ -51,13 +52,14 @@ See https://github.com/vangent/stravacli#upload-activities
5152
for detailed instructions.`,
5253
Args: cobra.NoArgs,
5354
RunE: func(_ *cobra.Command, _ []string) error {
54-
return doUpload(accessToken, inFile, dryRun)
55+
return checkPartialSuccess(doUpload(accessToken, inFile, startRow, dryRun))
5556
},
5657
}
5758
uploadCmd.Flags().StringVarP(&accessToken, "access_token", "t", "", "Strava access token; use the auth command to get one")
5859
uploadCmd.MarkFlagRequired("access_token")
5960
uploadCmd.Flags().StringVar(&inFile, "in", "", ".csv with activities to upload")
6061
uploadCmd.MarkFlagRequired("in")
62+
uploadCmd.Flags().IntVar(&startRow, "start_row", 1, "skip rows in the input up to this row (row 0 is the header row)")
6163
uploadCmd.Flags().BoolVar(&dryRun, "dryrun", false, "do a dry run: print out proposed changes")
6264
rootCmd.AddCommand(uploadCmd)
6365
}
@@ -152,29 +154,31 @@ func (a *uploadActivity) Verify() error {
152154
return nil
153155
}
154156

155-
func doUpload(accessToken, inFile string, dryRun bool) error {
157+
func doUpload(accessToken, inFile string, startRow int, dryRun bool) (int, error) {
156158
activities, err := loadActivitiesFromCSV(inFile)
157159
if err != nil {
158-
return err
160+
return 0, err
159161
}
160162

161163
ctx := context.WithValue(context.Background(), strava.ContextAccessToken, accessToken)
162164
uploadSvc := strava.NewAPIClient(strava.NewConfiguration()).UploadsApi
163165

164-
fmt.Printf("Found %d activities in %q to upload.\n", len(activities), inFile)
165-
nUploads := 0
166+
fmt.Printf("Found %d activities in %q to upload%s....\n", len(activities), inFile, startRowMessage(len(activities), startRow))
167+
n := 0
166168
for i, a := range activities {
169+
row := i + 1 // row 0 is the header row
170+
if row < startRow {
171+
continue
172+
}
167173
if err := uploadOne(ctx, uploadSvc, a, dryRun); err != nil {
168-
return fmt.Errorf("failed to upload activity %v near line %d: %v", a, i+1, err)
174+
return row, fmt.Errorf("failed to upload activity %v: %v", a, err)
169175
}
170-
nUploads++
176+
n++
171177
}
172-
if dryRun {
173-
fmt.Printf("Found %d activities to be uploaded.\n", nUploads)
174-
} else {
175-
fmt.Printf("Uploaded %d activities.\n", nUploads)
178+
if !dryRun {
179+
fmt.Printf("Uploaded %d activities.\n", n)
176180
}
177-
return nil
181+
return 0, nil
178182
}
179183

180184
func loadActivitiesFromCSV(filename string) ([]*uploadActivity, error) {
@@ -231,7 +235,7 @@ func uploadOne(ctx context.Context, uploadSvc *strava.UploadsApiService, a *uplo
231235
body, _ := ioutil.ReadAll(resp.Body)
232236
msg = string(body)
233237
}
234-
return fmt.Errorf("%v %v %s", err, resp, msg)
238+
return fmt.Errorf("%v %s", err, msg)
235239
}
236240
if upload.Error_ != "" {
237241
return fmt.Errorf("upload failed: %s", upload.Error_)

cmd/uploadmanual.go

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
func init() {
4040
var accessToken string
4141
var inFile string
42+
var startRow int
4243
var dryRun bool
4344

4445
uploadManualCmd := &cobra.Command{
@@ -50,13 +51,14 @@ See https://github.com/vangent/stravacli#upload-manual-activities
5051
for detailed instructions.`,
5152
Args: cobra.NoArgs,
5253
RunE: func(_ *cobra.Command, _ []string) error {
53-
return doUploadManual(accessToken, inFile, dryRun)
54+
return checkPartialSuccess(doUploadManual(accessToken, inFile, startRow, dryRun))
5455
},
5556
}
5657
uploadManualCmd.Flags().StringVarP(&accessToken, "access_token", "t", "", "Strava access token; use the auth command to get one")
5758
uploadManualCmd.MarkFlagRequired("access_token")
5859
uploadManualCmd.Flags().StringVar(&inFile, "in", "", ".csv with activities to upload")
5960
uploadManualCmd.MarkFlagRequired("in")
61+
uploadManualCmd.Flags().IntVar(&startRow, "start_row", 1, "skip rows in the input up to this row (row 0 is the header row)")
6062
uploadManualCmd.Flags().BoolVar(&dryRun, "dryrun", false, "do a dry run: print out proposed changes")
6163
rootCmd.AddCommand(uploadManualCmd)
6264
}
@@ -93,30 +95,31 @@ func (a *manualActivity) Verify() error {
9395
return nil
9496
}
9597

96-
func doUploadManual(accessToken, inFile string, dryRun bool) error {
97-
98+
func doUploadManual(accessToken, inFile string, startRow int, dryRun bool) (int, error) {
9899
activities, err := loadManualActivitiesFromCSV(inFile)
99100
if err != nil {
100-
return err
101+
return 0, err
101102
}
102103

103104
ctx := context.WithValue(context.Background(), strava.ContextAccessToken, accessToken)
104105
apiSvc := strava.NewAPIClient(strava.NewConfiguration()).ActivitiesApi
105106

106-
fmt.Printf("Found %d manual activities in %q to upload.\n", len(activities), inFile)
107-
nUploads := 0
107+
fmt.Printf("Found %d manual activities in %q to upload%s....\n", len(activities), inFile, startRowMessage(len(activities), startRow))
108+
n := 0
108109
for i, a := range activities {
110+
row := i + 1 // row 0 is the header row
111+
if row < startRow {
112+
continue
113+
}
109114
if err := uploadManualOne(ctx, apiSvc, a, dryRun); err != nil {
110-
return fmt.Errorf("failed to upload manual activity %v near line %d: %v", a, i+1, err)
115+
return row, fmt.Errorf("failed to upload manual activity %v: %v", a, err)
111116
}
112-
nUploads++
117+
n++
113118
}
114-
if dryRun {
115-
fmt.Printf("Found %d manual activities to be uploaded.\n", nUploads)
116-
} else {
117-
fmt.Printf("Uploaded %d manual activities.\n", nUploads)
119+
if !dryRun {
120+
fmt.Printf("Uploaded %d manual activities.\n", n)
118121
}
119-
return nil
122+
return 0, nil
120123
}
121124

122125
func loadManualActivitiesFromCSV(filename string) ([]*manualActivity, error) {

0 commit comments

Comments
 (0)