Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ linters:
- gocritic
- gocyclo
- gomoddirectives
- gomodguard
- gomodguard_v2
- goprintffuncname
- gosec
- govet
Expand Down Expand Up @@ -101,21 +101,20 @@ linters:
paramsOnly: false
underef:
skipRecvDeref: false
gomodguard:
gomodguard_v2:
blocked:
modules:
- github.com/golang/protobuf:
recommendations:
- google.golang.org/protobuf
reason: see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules
- github.com/satori/go.uuid:
recommendations:
- github.com/google/uuid
reason: satori's package is not maintained
- github.com/gofrs/uuid:
recommendations:
- github.com/google/uuid
reason: gofrs' package is not go module
- module: github.com/golang/protobuf
recommendations:
- google.golang.org/protobuf
reason: see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules
- module: github.com/satori/go.uuid
recommendations:
- github.com/google/uuid
reason: satori's package is not maintained
- module: github.com/gofrs/uuid
recommendations:
- github.com/google/uuid
reason: gofrs' package is not go module
govet:
disable:
- fieldalignment
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/bzimmer/activity

go 1.26.2
go 1.26.3

require (
github.com/bzimmer/httpwares v0.1.3
Expand Down
19 changes: 0 additions & 19 deletions strava/activity.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"net/url"
"regexp"
"strings"
"time"

"golang.org/x/sync/errgroup"

Expand All @@ -28,24 +27,6 @@ type ActivityIterFunc func(*Activity) (bool, error)
// fileNameRE allowable characters
var fileNameRE = regexp.MustCompile("[A-Za-z0-9-]+")

// WithDateRange sets the before and after date range
func WithDateRange(before, after time.Time) APIOption {
return func(v url.Values) error {
if !before.IsZero() && !after.IsZero() {
if after.After(before) {
return errors.New("invalid date range")
}
}
if !before.IsZero() {
v.Set("before", fmt.Sprintf("%d", before.Unix()))
}
if !after.IsZero() {
v.Set("after", fmt.Sprintf("%d", after.Unix()))
}
return nil
}
}

type channelPaginator struct {
count int
options []APIOption
Expand Down
8 changes: 5 additions & 3 deletions strava/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
var _ activity.GPXEncoder = (*Route)(nil)
var _ activity.GPXEncoder = (*Activity)(nil)

const gpxVersion = "1.1"

func polylineToLineString(polylines ...string) (*geom.LineString, error) {
const n = 2
var coords []float64
Expand Down Expand Up @@ -58,7 +60,7 @@ func (a *Activity) GPX() (*gpx.GPX, error) {
},
}
x := &gpx.GPX{
Version: "1.1",
Version: gpxVersion,
Trk: []*gpx.TrkType{trk},
}
return x, nil
Expand All @@ -81,7 +83,7 @@ func (r *Route) GPX() (*gpx.GPX, error) {
},
}
x := &gpx.GPX{
Version: "1.1",
Version: gpxVersion,
Rte: []*gpx.RteType{rte},
}
return x, nil
Expand All @@ -106,7 +108,7 @@ func (a *Activity) toGPXFromStreams() (*gpx.GPX, error) {
}
}
x := &gpx.GPX{
Version: "1.1",
Version: gpxVersion,
Trk: []*gpx.TrkType{
{
Name: a.Name,
Expand Down
101 changes: 101 additions & 0 deletions strava/segment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package strava

import (
"context"
"fmt"
"net/http"
"net/url"

"github.com/bzimmer/activity"
)

// SegmentService is the API for segment effort endpoints.
type SegmentService service

// SegmentEffortIterFunc is called for each segment effort in the results.
type SegmentEffortIterFunc func(*SegmentEffort) (bool, error)

type segmentPaginator struct {
segmentEfforts []*SegmentEffort
service SegmentService
options []APIOption
}

func (p *segmentPaginator) PageSize() int {
return PageSize
}

func (p *segmentPaginator) Count() int {
return len(p.segmentEfforts)
}

func (p *segmentPaginator) Do(ctx context.Context, spec activity.Pagination) (int, error) {
v := make(url.Values)
v.Set("page", fmt.Sprintf("%d", spec.Start))
v.Set("per_page", fmt.Sprintf("%d", spec.Count))
for _, opt := range p.options {
if opt == nil {
continue
}
if err := opt(v); err != nil {
return 0, err
}
}
uri := fmt.Sprintf("segment_efforts?%s", v.Encode())
req, err := p.service.client.newAPIRequest(ctx, http.MethodGet, uri, nil)
if err != nil {
return 0, err
}
var segs []*SegmentEffort
err = p.service.client.do(req, &segs)
if err != nil {
return 0, err
}
if spec.Total > 0 && len(p.segmentEfforts)+len(segs) > spec.Total {
segs = segs[:spec.Total-len(p.segmentEfforts)]
}
p.segmentEfforts = append(p.segmentEfforts, segs...)
return len(segs), nil
}

// SegmentEfforts returns a page of segment efforts for the authenticated athlete.
//
// The returned segment efforts can be filtered by date using WithDateRange(before, after).
func (s *SegmentService) SegmentEfforts(
ctx context.Context, spec activity.Pagination, opts ...APIOption) ([]*SegmentEffort, error) {
p := &segmentPaginator{service: *s, segmentEfforts: make([]*SegmentEffort, 0), options: opts}
err := activity.Paginate(ctx, p, spec)
if err != nil {
return nil, err
}
return p.segmentEfforts, nil
}

// SegmentEffort returns a segment effort.
func (s *SegmentService) SegmentEffort(ctx context.Context, segmentEffortID int64) (*SegmentEffort, error) {
uri := fmt.Sprintf("segment_efforts/%d", segmentEffortID)
req, err := s.client.newAPIRequest(ctx, http.MethodGet, uri, nil)
if err != nil {
return nil, err
}
seg := &SegmentEffort{}
err = s.client.do(req, &seg)
if err != nil {
return nil, err
}
return seg, nil
}

// SegmentEffortsIter executes the iter function over segment effort results.
func SegmentEffortsIter(segmentEfforts []*SegmentEffort, iter SegmentEffortIterFunc) error {
for _, segmentEffort := range segmentEfforts {
ok, err := iter(segmentEffort)
if err != nil {
return err
}
if !ok {
return nil
}
}
return nil
}
Loading
Loading