@@ -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
128134func 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 \n Some 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+ }
0 commit comments