mirror of
https://github.com/sstent/GarminSync.git
synced 2026-01-26 00:52:32 +00:00
go
This commit is contained in:
153
internal/db/database.go
Normal file
153
internal/db/database.go
Normal file
@@ -0,0 +1,153 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/mattn/go-sqlite3"
|
||||
"github.com/sstent/garminsync/internal/garmin"
|
||||
)
|
||||
|
||||
// SQLiteDatabase implements ActivityRepository using SQLite
|
||||
type SQLiteDatabase struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewDatabase creates a new SQLite database connection
|
||||
func NewDatabase(path string) (*SQLiteDatabase, error) {
|
||||
db, err := sql.Open("sqlite3", path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open database: %w", err)
|
||||
}
|
||||
|
||||
// Create table if it doesn't exist
|
||||
if err := createSchema(db); err != nil {
|
||||
return nil, fmt.Errorf("failed to create schema: %w", err)
|
||||
}
|
||||
|
||||
return &SQLiteDatabase{db: db}, nil
|
||||
}
|
||||
|
||||
// Close closes the database connection
|
||||
func (d *SQLiteDatabase) Close() error {
|
||||
return d.db.Close()
|
||||
}
|
||||
|
||||
// createSchema creates the database schema
|
||||
func createSchema(db *sql.DB) error {
|
||||
schema := `
|
||||
CREATE TABLE IF NOT EXISTS activities (
|
||||
activity_id INTEGER PRIMARY KEY,
|
||||
start_time TEXT NOT NULL,
|
||||
filename TEXT NOT NULL,
|
||||
downloaded BOOLEAN NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_activity_id ON activities(activity_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_downloaded ON activities(downloaded);
|
||||
`
|
||||
|
||||
if _, err := db.Exec(schema); err != nil {
|
||||
return fmt.Errorf("failed to create schema: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAll returns all activities from the database
|
||||
func (d *SQLiteDatabase) GetAll() ([]garmin.Activity, error) {
|
||||
return d.GetAllPaginated(0, 0) // 0,0 means no pagination
|
||||
}
|
||||
|
||||
// GetMissing returns activities that haven't been downloaded yet
|
||||
func (d *SQLiteDatabase) GetMissing() ([]garmin.Activity, error) {
|
||||
return d.GetMissingPaginated(0, 0)
|
||||
}
|
||||
|
||||
// GetDownloaded returns activities that have been downloaded
|
||||
func (d *SQLiteDatabase) GetDownloaded() ([]garmin.Activity, error) {
|
||||
return d.GetDownloadedPaginated(0, 0)
|
||||
}
|
||||
|
||||
// GetAllPaginated returns a paginated list of all activities
|
||||
func (d *SQLiteDatabase) GetAllPaginated(page, pageSize int) ([]garmin.Activity, error) {
|
||||
offset := (page - 1) * pageSize
|
||||
query := "SELECT activity_id, start_time, filename, downloaded FROM activities"
|
||||
if pageSize > 0 {
|
||||
query += fmt.Sprintf(" LIMIT %d OFFSET %d", pageSize, offset)
|
||||
}
|
||||
rows, err := d.db.Query(query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get all activities: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
return scanActivities(rows)
|
||||
}
|
||||
|
||||
// GetMissingPaginated returns a paginated list of missing activities
|
||||
func (d *SQLiteDatabase) GetMissingPaginated(page, pageSize int) ([]garmin.Activity, error) {
|
||||
offset := (page - 1) * pageSize
|
||||
query := "SELECT activity_id, start_time, filename, downloaded FROM activities WHERE downloaded = 0"
|
||||
if pageSize > 0 {
|
||||
query += fmt.Sprintf(" LIMIT %d OFFSET %d", pageSize, offset)
|
||||
}
|
||||
rows, err := d.db.Query(query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get missing activities: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
return scanActivities(rows)
|
||||
}
|
||||
|
||||
// GetDownloadedPaginated returns a paginated list of downloaded activities
|
||||
func (d *SQLiteDatabase) GetDownloadedPaginated(page, pageSize int) ([]garmin.Activity, error) {
|
||||
offset := (page - 1) * pageSize
|
||||
query := "SELECT activity_id, start_time, filename, downloaded FROM activities WHERE downloaded = 1"
|
||||
if pageSize > 0 {
|
||||
query += fmt.Sprintf(" LIMIT %d OFFSET %d", pageSize, offset)
|
||||
}
|
||||
rows, err := d.db.Query(query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get downloaded activities: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
return scanActivities(rows)
|
||||
}
|
||||
|
||||
// MarkDownloaded updates the database when an activity is downloaded
|
||||
func (d *SQLiteDatabase) MarkDownloaded(activityId int, filename string) error {
|
||||
_, err := d.db.Exec("UPDATE activities SET downloaded = 1, filename = ? WHERE activity_id = ?",
|
||||
filename, activityId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to mark activity as downloaded: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// scanActivities converts database rows to Activity objects
|
||||
func scanActivities(rows *sql.Rows) ([]garmin.Activity, error) {
|
||||
var activities []garmin.Activity
|
||||
|
||||
for rows.Next() {
|
||||
var activity garmin.Activity
|
||||
var downloaded int
|
||||
var startTime string
|
||||
|
||||
if err := rows.Scan(&activity.ActivityId, &startTime, &activity.Filename, &downloaded); err != nil {
|
||||
return nil, fmt.Errorf("failed to scan activity: %w", err)
|
||||
}
|
||||
|
||||
// Convert SQLite time string to time.Time
|
||||
activity.StartTime, _ = time.Parse("2006-01-02 15:04:05", startTime)
|
||||
activity.Downloaded = downloaded == 1
|
||||
activities = append(activities, activity)
|
||||
}
|
||||
|
||||
return activities, nil
|
||||
}
|
||||
Reference in New Issue
Block a user