Skip to content

Commit

Permalink
autogenerate name & maxElevation in the elevation profile (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
nce authored Oct 8, 2024
1 parent 531f805 commit a38a160
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 155 deletions.
143 changes: 103 additions & 40 deletions pkg/activity/header.go
Original file line number Diff line number Diff line change
@@ -1,51 +1,114 @@
package activity

import (
"errors"
"fmt"
"os"
"reflect"
"strings"

"gopkg.in/yaml.v3"
)

var (
ErrFieldNotFound = errors.New("field not found in struct")
ErrTypeNotAssert = errors.New("could not assert type in struct")
)

type Header struct {
Meta HeaderMeta `yaml:"meta"`
Activity HeaderActivity `yaml:"activity"`
Layout Layout `yaml:"layout"`
Stats Stats `yaml:"stats"`
Meta struct {
Version string `yaml:"version,omitempty"`
} `yaml:"meta"`
Activity struct {
Wandern bool `yaml:"wandern,omitempty"`
Skitour bool `yaml:"skitour,omitempty"`
MTB bool `yaml:"mtb,omitempty"`
Type string `yaml:"type"`
Date string `yaml:"date"`
Title string `yaml:"title"`
PointOfOrigin struct {
Name string `yaml:"name"`
Qr string `yaml:"qr"`
Region string `yaml:"region"`
} `yaml:"pointOfOrigin"`
Season string `yaml:"season"`
Rating string `yaml:"rating"`
Company string `yaml:"company"`
Restaurant string `yaml:"restaurant"`
Difficulty string `yaml:"difficulty,omitempty"`
LLB string `yaml:"llb,omitempty"`
MaxElevation string `yaml:"maxElevation"`
} `yaml:"activity"`
Layout struct {
HeadElevationProfile bool `yaml:"headElevationProfile"`
ElevationProfileType string `yaml:"elevationProfileType,omitempty"`
ElevationProfileRightMargin float32 `yaml:"elevationProfileRightMargin"`
TableSize float32 `yaml:"tableSize"`
MapSize float32 `yaml:"mapSize"`
MapHeight int `yaml:"mapHeight"`
Linespread float32 `yaml:"linespread"`
} `yaml:"layout"`
Stats struct {
Ascent string `yaml:"ascent"`
Distance string `yaml:"distance"`
MovingTime string `yaml:"movingTime"`
OverallTime string `yaml:"overallTime"`
StartTime string `yaml:"startTime"`
SummitTime string `yaml:"summitTime"`
Puls string `yaml:"puls,omitempty"`
} `yaml:"stats"`
}

type HeaderMeta struct {
Version string `yaml:"version,omitempty"`
}
func GetFromHeader[T any](dir string, field string) (T, error) { //nolint:ireturn
var zero T

type HeaderActivity struct {
Wandern bool `yaml:"wandern,omitempty"`
Type string `yaml:"type"`
Date string `yaml:"date"`
Title string `yaml:"title"`
PointOfOrigin PointOfOrigin `yaml:"pointOfOrigin"`
Season string `yaml:"season"`
Rating string `yaml:"rating"`
Company string `yaml:"company"`
Restaurant string `yaml:"restaurant"`
MaxElevation string `yaml:"maxElevation"`
}
data, err := os.ReadFile(dir + "/header.yaml")
if err != nil {
return zero, fmt.Errorf("error reading file: %w", err)
}

type PointOfOrigin struct {
Name string `yaml:"name"`
Qr string `yaml:"qr"`
Region string `yaml:"region"`
}
var act Header

err = yaml.Unmarshal(data, &act)
if err != nil {
return zero, fmt.Errorf("error unmarshalling YAML: %w", err)
}

type Stats struct {
Ascent string `yaml:"ascent"`
Distance string `yaml:"distance"`
MovingTime string `yaml:"movingTime"`
OverallTime string `yaml:"overallTime"`
StartTime string `yaml:"startTime"`
SummitTime string `yaml:"summitTime"`
Puls string `yaml:"puls,omitempty"`
value, err := searchField[T](&act, field)
if err != nil {
return zero, fmt.Errorf("error searching field: %w", err)
}

return value, nil
}

type Layout struct {
HeadElevationProfile bool `yaml:"headElevationProfile"`
ElevationProfileType string `yaml:"elevationProfileType,omitempty"`
ElevationProfileRightMargin float32 `yaml:"elevationProfileRightMargin"`
TableSize float32 `yaml:"tableSize"`
MapSize float32 `yaml:"mapSize"`
MapHeight int `yaml:"mapHeight"`
Linespread float32 `yaml:"linespread"`
func searchField[T any](v interface{}, path string) (T, error) { //nolint:ireturn
keys := strings.Split(path, ".")
header := reflect.ValueOf(v)

// Traverse the struct hierarchy using the keys
for _, key := range keys {
// Check if we are dealing with a pointer and dereference it
if header.Kind() == reflect.Ptr {
header = header.Elem()
}

// Get the field by name
header = header.FieldByName(key)
if !header.IsValid() {
var zero T

return zero, fmt.Errorf("field %s not found: %w", key, ErrFieldNotFound)
}
}

// Type assert the value to the desired type T
value, ok := header.Interface().(T)
if !ok {
var zero T

return zero, fmt.Errorf("field %s cannot be asserted to the expected type: %w", path, ErrTypeNotAssert)
}

return value, nil
}
26 changes: 23 additions & 3 deletions pkg/render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"text/template"
"time"

"github.com/nce/tourenbuchctl/pkg/activity"
"github.com/nce/tourenbuchctl/pkg/utils"
"github.com/rs/zerolog/log"
)
Expand All @@ -26,6 +27,8 @@ type PageOpts struct {
ActivityType string
ElevationProfileType string
SaveToDisk bool
MaxElevation string
ActivityTitle string
}

const (
Expand Down Expand Up @@ -151,9 +154,14 @@ func (n *PageOpts) generatElevationProfile() error {
return fmt.Errorf("failed to prepare elevation profile: %w", err)
}

//nolint: gosec
cmd := exec.Command(
"gnuplot",
"master.plt")
"-c",
"master.plt",
n.ActivityTitle,
n.MaxElevation,
)

cmd.Dir = n.AbsoluteTextDir + n.RelativeCwd + "/" + n.TmpDir
cmd.Stdout = os.Stdout
Expand Down Expand Up @@ -198,12 +206,22 @@ func NewPage(cwd string, saveToDisk bool) (*PageOpts, error) {
return nil, fmt.Errorf("failed to split activity directory name: %w", err)
}

activityType, err := utils.ReadActivityTypeFromHeader(cwd)
activityType, err := activity.GetFromHeader[string](cwd, "Activity.Type")
if err != nil {
return nil, fmt.Errorf("failed to read activity type: %w", err)
}

elevationProfileType, err := activity.GetFromHeader[string](cwd, "Layout.ElevationProfileType")
if err != nil {
return nil, fmt.Errorf("failed to read elevationProfileType: %w", err)
}

maxElevation, err := activity.GetFromHeader[string](cwd, "Activity.MaxElevation")
if err != nil {
return nil, fmt.Errorf("failed to read activity type: %w", err)
}

elevationProfileType, err := utils.ReadElevationProfileTypeFromHeader(cwd)
activityTitle, err := activity.GetFromHeader[string](cwd, "Activity.Title")
if err != nil {
return nil, fmt.Errorf("failed to read activity type: %w", err)
}
Expand All @@ -218,6 +236,8 @@ func NewPage(cwd string, saveToDisk bool) (*PageOpts, error) {
ActivityDate: date,
ActivityType: activityType,
ElevationProfileType: elevationProfileType,
MaxElevation: maxElevation,
ActivityTitle: activityTitle,
}, nil
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/render/templates/elevationprofile/left-axis-layout.plt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ myScaleHM = {{ .Activity }}HM
myTics = {{ .Activity }}Tics
myColor = {{ .Activity }}Color

title=ARG1
maxElevation=ARG2

file = 'gpxdata.txt'

reset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ myScaleHM = {{ .Activity }}HM
myTics = {{ .Activity }}Tics
myColor = {{ .Activity }}Color

title=ARG1
maxElevation=ARG2

file = 'gpxdata.txt'

reset
Expand Down
47 changes: 0 additions & 47 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import (
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
"regexp"
"time"

"golang.org/x/oauth2"
"gopkg.in/yaml.v3"
)

type Token struct {
Expand All @@ -35,11 +33,6 @@ type Layout struct {
ElevationProfileType string `yaml:"elevationProfileType"`
}

var (
ErrActivityTypeEmpty = errors.New("Activity.Type is empty")
ErrLayoutElevationProfileTypeEmpty = errors.New("Layout.ElevationProfileType is empty")
)

func SaveToken(filename string, token *oauth2.Token) error {
file, err := os.Create(filename)
if err != nil {
Expand Down Expand Up @@ -154,43 +147,3 @@ func SplitActivityDirectoryName(dirName string) (string, string, error) {

return namePart, datePart, nil
}

func ReadActivityTypeFromHeader(dirName string) (string, error) {
data, err := os.ReadFile(dirName + "/header.yaml")
if err != nil {
return "", fmt.Errorf("error reading file: %w", err)
}

var act Header

err = yaml.Unmarshal(data, &act)
if err != nil {
return "", fmt.Errorf("error unmarshalling YAML: %w", err)
}

if act.Activity.Type == "" {
return "", fmt.Errorf("parsing %s/header.yaml: %w", dirName, ErrActivityTypeEmpty)
}

return act.Activity.Type, nil
}

func ReadElevationProfileTypeFromHeader(dirName string) (string, error) {
data, err := os.ReadFile(dirName + "/header.yaml")
if err != nil {
return "", fmt.Errorf("error reading file: %w", err)
}

var act Header

err = yaml.Unmarshal(data, &act)
if err != nil {
return "", fmt.Errorf("error unmarshalling YAML: %w", err)
}

if act.Layout.ElevationProfileType == "" {
return "", fmt.Errorf("parsing %s/header.yaml: %w", dirName, ErrLayoutElevationProfileTypeEmpty)
}

return act.Layout.ElevationProfileType, nil
}
65 changes: 0 additions & 65 deletions pkg/utils/utils_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package utils

import (
"fmt"
"os"
"testing"
)

Expand Down Expand Up @@ -33,66 +31,3 @@ func TestSplitDirectoryName(t *testing.T) {
})
}
}

func TestReadActivityTypeFromHeader(t *testing.T) {
t.Parallel()

dirName := t.TempDir()
expected := "running"

// Create a test header.yaml file in the testdata directory
err := createTestHeaderFile(dirName, expected, "foo")
if err != nil {
t.Fatalf("error creating test file: %v", err)
}

// Test reading activity type from header.yaml
activityType, err := ReadActivityTypeFromHeader(dirName)
if err != nil {
t.Fatalf("error reading activity type: %v", err)
}

if activityType != expected {
t.Errorf("expected activity type %s, got %s", expected, activityType)
}
}

func TestReadElevationProfileTypeFromHeader(t *testing.T) {
t.Parallel()

dirName := t.TempDir()
expected := "left-axis"

// Create a test header.yaml file in the testdata directory
err := createTestHeaderFile(dirName, "foo", expected)
if err != nil {
t.Fatalf("error creating test file: %v", err)
}

// Test reading activity type from header.yaml
elevationProfileType, err := ReadElevationProfileTypeFromHeader(dirName)
if err != nil {
t.Fatalf("error reading elevationProfileType: %v", err)
}

if elevationProfileType != expected {
t.Errorf("expected layout elevationProfileType %s, got %s", expected, elevationProfileType)
}
}

func createTestHeaderFile(dirName, activityType string, elevationProfileType string) error {
headerContent := []byte(`
activity:
type: ` + activityType + `
layout:
elevationProfileType: ` + elevationProfileType + `
`)

//nolint: gosec
err := os.WriteFile(dirName+"/header.yaml", headerContent, 0o644)
if err != nil {
return fmt.Errorf("error setting up header.yaml: %w", err)
}

return nil
}

0 comments on commit a38a160

Please sign in to comment.