151 lines
3.7 KiB
Go
151 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"image/color"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"fyne.io/fyne/v2"
|
|
"fyne.io/fyne/v2/container"
|
|
"fyne.io/fyne/v2/theme"
|
|
"fyne.io/x/fyne/widget/charts"
|
|
"go.arsenm.dev/itd/api"
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
func graphTab(ctx context.Context, client *api.Client, w fyne.Window) fyne.CanvasObject {
|
|
// Get user configuration directory
|
|
userCfgDir, err := os.UserConfigDir()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
cfgDir := filepath.Join(userCfgDir, "itd")
|
|
dbPath := filepath.Join(cfgDir, "metrics.db")
|
|
|
|
// If stat on database returns error, return nil
|
|
if _, err := os.Stat(dbPath); err != nil {
|
|
return nil
|
|
}
|
|
|
|
// Open database
|
|
db, err := sql.Open("sqlite", dbPath)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
// Get heart rate data and create chart
|
|
heartRateData := getData(db, "bpm", "heartRate")
|
|
heartRate := newLineChartData(nil, heartRateData)
|
|
|
|
// Get step count data and create chart
|
|
stepCountData := getData(db, "steps", "stepCount")
|
|
stepCount := newLineChartData(nil, stepCountData)
|
|
|
|
// Get battery level data and create chart
|
|
battLevelData := getData(db, "percent", "battLevel")
|
|
battLevel := newLineChartData(nil, battLevelData)
|
|
|
|
// Get motion data
|
|
motionData := getMotionData(db)
|
|
// Create chart for each coordinate
|
|
xChart := newLineChartData(theme.PrimaryColorNamed(theme.ColorRed), motionData["X"])
|
|
yChart := newLineChartData(theme.PrimaryColorNamed(theme.ColorGreen), motionData["Y"])
|
|
zChart := newLineChartData(theme.PrimaryColorNamed(theme.ColorBlue), motionData["Z"])
|
|
|
|
// Create new max container with all the charts
|
|
motion := container.NewMax(xChart, yChart, zChart)
|
|
|
|
// Create tabs for charts
|
|
chartTabs := container.NewAppTabs(
|
|
container.NewTabItem("Heart Rate", heartRate),
|
|
container.NewTabItem("Step Count", stepCount),
|
|
container.NewTabItem("Battery Level", battLevel),
|
|
container.NewTabItem("Motion", motion),
|
|
)
|
|
// Place tabs on left
|
|
chartTabs.SetTabLocation(container.TabLocationLeading)
|
|
return chartTabs
|
|
}
|
|
|
|
func newLineChartData(col color.Color, data []float64) *charts.LineChart {
|
|
// Create new line chart
|
|
lc := charts.NewLineChart(nil)
|
|
setOpts(lc, col)
|
|
// If no data, make the stroke transparent
|
|
if len(data) == 0 {
|
|
lc.Options().StrokeColor = color.RGBA{0, 0, 0, 0}
|
|
}
|
|
// Set data
|
|
lc.SetData(data)
|
|
return lc
|
|
}
|
|
|
|
func setOpts(lc *charts.LineChart, col color.Color) {
|
|
// Get pointer to options
|
|
opts := lc.Options()
|
|
// Set fill color to transparent
|
|
opts.FillColor = color.RGBA{0, 0, 0, 0}
|
|
// Set stroke width
|
|
opts.StrokeWidth = 2
|
|
// If color provided
|
|
if col != nil {
|
|
// Set stroke color
|
|
opts.StrokeColor = col
|
|
} else {
|
|
// Set stroke color to orange primary color
|
|
opts.StrokeColor = theme.PrimaryColorNamed(theme.ColorOrange)
|
|
}
|
|
}
|
|
|
|
func getData(db *sql.DB, field, table string) []float64 {
|
|
// Get data from database
|
|
rows, err := db.Query("SELECT " + field + " FROM " + table + " ORDER BY time;")
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer rows.Close()
|
|
|
|
var out []float64
|
|
for rows.Next() {
|
|
var val int64
|
|
// Scan data into int
|
|
err := rows.Scan(&val)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
// Convert to float64 and append to data slice
|
|
out = append(out, float64(val))
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func getMotionData(db *sql.DB) map[string][]float64 {
|
|
// Get data from database
|
|
rows, err := db.Query("SELECT X, Y, Z FROM motion ORDER BY time;")
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer rows.Close()
|
|
|
|
out := map[string][]float64{}
|
|
for rows.Next() {
|
|
var x, y, z int64
|
|
// Scan data into ints
|
|
err := rows.Scan(&x, &y, &z)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
// Convert to float64 and append to appropriate slice
|
|
out["X"] = append(out["X"], float64(x))
|
|
out["Y"] = append(out["Y"], float64(y))
|
|
out["Z"] = append(out["Z"], float64(z))
|
|
}
|
|
|
|
return out
|
|
}
|