Compare commits
No commits in common. "61a4c0dee65acd5d7941ef73059bf68017691677" and "da5c4ce2a97de3f14ccbcfe3f8cf44fe81258930" have entirely different histories.
61a4c0dee6
...
da5c4ce2a9
@ -22,11 +22,3 @@ Reading data will be saved to `$HOME/.ketotrack/readings.json`.
|
|||||||
go build
|
go build
|
||||||
mv ketotrack $HOME/.local/bin
|
mv ketotrack $HOME/.local/bin
|
||||||
```
|
```
|
||||||
|
|
||||||
## Refactoring Notes
|
|
||||||
- [X] Create `Record` and `Note` struct types
|
|
||||||
- [X] Add `GKI` field to `Reading` struct type
|
|
||||||
- [X] Encapsulate application data with an `AppContext` struct that holds a `Records` slice
|
|
||||||
- [X] `LoadRecords`, `SaveRecords`, `AddReading`, `AddNote` receiver methods
|
|
||||||
- [X] Add interactive menu for adding either a `Note` or a `Record`
|
|
||||||
- [ ] Standardize method names for getting user input to `handleNewNote` and `handleNewReading`
|
|
167
main.go
167
main.go
@ -12,7 +12,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var recordsFilename string
|
var readingsFilename string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Get user's homeDir directory
|
// Get user's homeDir directory
|
||||||
@ -33,27 +33,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set data file path for readings
|
// Set data file path for readings
|
||||||
recordsFilename = filepath.Join(ketotrackDir, "records.json")
|
readingsFilename = filepath.Join(ketotrackDir, "readings.json")
|
||||||
}
|
|
||||||
|
|
||||||
// Record holds a pointer to a Reading or Note.
|
|
||||||
type Record struct {
|
|
||||||
Reading *Reading `json:"reading,omitempty"`
|
|
||||||
Note *Note `json:"note,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note holds a text note along with the time the note was taken.
|
|
||||||
type Note struct {
|
|
||||||
Time time.Time `json:"time"`
|
|
||||||
Text string `json:"text"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNote returns a new Note
|
|
||||||
func NewNote(text string) Note {
|
|
||||||
return Note{
|
|
||||||
Time: time.Now(),
|
|
||||||
Text: text,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reading holds the glucose and ketone level measurements along with the time the measurements were taken.
|
// Reading holds the glucose and ketone level measurements along with the time the measurements were taken.
|
||||||
@ -61,124 +41,89 @@ type Reading struct {
|
|||||||
Time time.Time `json:"time"`
|
Time time.Time `json:"time"`
|
||||||
Glucose float64 `json:"glucose"`
|
Glucose float64 `json:"glucose"`
|
||||||
Ketone float64 `json:"ketone"`
|
Ketone float64 `json:"ketone"`
|
||||||
GKI float64 `json:"GKI"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReading creates and returns a new Reading instance with the provided glucose and ketone levels, and records
|
// NewReading creates and returns a new Reading instance with the provided glucose and ketone levels, and records
|
||||||
// the current time. The glucose value should be provided in mg/dL, while the ketone level should be in mmol/L.
|
// the current time. The glucose value should be provided in mg/dL, while the ketone level should be in mmol/L.
|
||||||
func NewReading(glucose, ketone float64) Reading {
|
func NewReading(glucose, ketone float64) Reading {
|
||||||
var gki float64
|
|
||||||
if ketone == 0 {
|
|
||||||
gki = 0
|
|
||||||
} else {
|
|
||||||
gki = (glucose / 18) / ketone
|
|
||||||
}
|
|
||||||
|
|
||||||
return Reading{
|
return Reading{
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
Glucose: glucose,
|
Glucose: glucose,
|
||||||
Ketone: ketone,
|
Ketone: ketone,
|
||||||
GKI: gki,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppContext is the application data store that holds Records
|
// GKI calculates and returns the Glucose Ketone Index of the reading.
|
||||||
type AppContext struct {
|
// The Glucose Ketone Index helps determine the efficiency of glucose and ketone levels within the body. It is
|
||||||
Records []Record
|
// calculated as (glucose level in mg/dL / 18) divided by the ketone level.
|
||||||
|
func (r Reading) GKI() (float64, error) {
|
||||||
|
if r.Ketone == 0 {
|
||||||
|
return 0, errors.New("ketone level must not be zero to calculate GKI")
|
||||||
|
}
|
||||||
|
return (r.Glucose / 18) / r.Ketone, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadRecords will load records from file
|
func main() {
|
||||||
func (ctx *AppContext) LoadRecords(filename string) error {
|
// Take a new reading from the user
|
||||||
data, err := os.ReadFile(filename)
|
reading, err := getReading()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
fmt.Println(err.Error())
|
||||||
}
|
return
|
||||||
return json.Unmarshal(data, &ctx.Records)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveRecords will save records to a file
|
// Calculate GKI
|
||||||
func (ctx *AppContext) SaveRecords(filename string) error {
|
gki, err := reading.GKI()
|
||||||
jsonData, err := json.Marshal(ctx.Records)
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
fmt.Println("You are not in ketosis.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("\nYour GKI is: %0.2f\n", gki)
|
||||||
|
|
||||||
|
// Display level of ketosis
|
||||||
|
switch {
|
||||||
|
case gki <= 1:
|
||||||
|
fmt.Println("You're in the highest level of ketosis.")
|
||||||
|
case gki < 3:
|
||||||
|
fmt.Println("You're in a high therapeutic level of ketosis.")
|
||||||
|
case gki < 6:
|
||||||
|
fmt.Println("You're in a moderate level of ketosis.")
|
||||||
|
case gki <= 9:
|
||||||
|
fmt.Println("You're in a low level of ketosis.")
|
||||||
|
default:
|
||||||
|
fmt.Println("You are not in ketosis.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save latest reading to readings file
|
||||||
|
readings, _ := loadReadingsFromFile(readingsFilename)
|
||||||
|
readings = append(readings, reading)
|
||||||
|
err = saveReadingsToFile(readingsFilename, readings)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("error saving data: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveReadingsToFile(filename string, data []Reading) error {
|
||||||
|
jsonData, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return os.WriteFile(filename, jsonData, 0660)
|
return os.WriteFile(filename, jsonData, 0660)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddReading will add a Reading to the AppContext
|
func loadReadingsFromFile(filename string) ([]Reading, error) {
|
||||||
func (ctx *AppContext) AddReading(reading Reading) {
|
data, err := os.ReadFile(filename)
|
||||||
ctx.Records = append(ctx.Records, Record{Reading: &reading})
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddNote will a add a Note to the AppContext
|
|
||||||
func (ctx *AppContext) AddNote(note Note) {
|
|
||||||
ctx.Records = append(ctx.Records, Record{Note: ¬e})
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// TODO: Clean up menu prompt
|
|
||||||
// TODO: Write getNote method
|
|
||||||
// TODO: Write option handlers (handleNewNote, handleNewReading)
|
|
||||||
|
|
||||||
var appCtx AppContext
|
|
||||||
|
|
||||||
if err := appCtx.LoadRecords(recordsFilename); err != nil {
|
|
||||||
fmt.Printf("Error loading records from file: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
choice, err := getUserInput("1. Enter new reading\n2. Enter new note\n3. Exit\nYour choice")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error reading choice: %s\n", err)
|
return nil, err
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if choice == "3" {
|
var readings []Reading
|
||||||
fmt.Println("Exiting...")
|
err = json.Unmarshal(data, &readings)
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
switch choice {
|
|
||||||
case "1":
|
|
||||||
// Get a new Reading from the user
|
|
||||||
reading, err := getReading()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error getting reading: %s\n", err)
|
return nil, err
|
||||||
} else {
|
|
||||||
// Display GKI and level of ketosis
|
|
||||||
fmt.Printf("\nYour GKI is: %0.2f\n", reading.GKI)
|
|
||||||
switch {
|
|
||||||
case reading.GKI <= 1:
|
|
||||||
fmt.Println("You're in the highest level of ketosis.")
|
|
||||||
case reading.GKI < 3:
|
|
||||||
fmt.Println("You're in a high therapeutic level of ketosis.")
|
|
||||||
case reading.GKI < 6:
|
|
||||||
fmt.Println("You're in a moderate level of ketosis.")
|
|
||||||
case reading.GKI <= 9:
|
|
||||||
fmt.Println("You're in a low level of ketosis.")
|
|
||||||
default:
|
|
||||||
fmt.Println("You are not in ketosis.")
|
|
||||||
}
|
|
||||||
appCtx.AddReading(reading)
|
|
||||||
}
|
|
||||||
case "2":
|
|
||||||
// Get a new Note from the user
|
|
||||||
noteText, err := getUserInput("Enter your note text: ")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error getting note: %s\n", err)
|
|
||||||
} else {
|
|
||||||
note := NewNote(noteText)
|
|
||||||
appCtx.AddNote(note)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
fmt.Printf("Invalid choice, please try again.\n\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save Records before exiting
|
return readings, nil
|
||||||
if err := appCtx.SaveRecords(recordsFilename); err != nil {
|
|
||||||
fmt.Printf("Error saving records to file: %s\n", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getReading() (Reading, error) {
|
func getReading() (Reading, error) {
|
||||||
|
20
main_test.go
Normal file
20
main_test.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGKI(t *testing.T) {
|
||||||
|
r := Reading{
|
||||||
|
Glucose: 77,
|
||||||
|
Ketone: 1.1,
|
||||||
|
}
|
||||||
|
expected := (77.0 / 18) / 1.1
|
||||||
|
epsilon := 0.0001 // Tolerance level
|
||||||
|
gki, _ := r.GKI()
|
||||||
|
|
||||||
|
if math.Abs(gki-expected) > epsilon {
|
||||||
|
t.Errorf("expected %v, got %v", expected, gki)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user