summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2022-10-03 23:33:19 -0400
committerAndrew Cady <d@jerkface.net>2022-10-03 23:33:52 -0400
commit5857d01a6badd38f9a216d090b047474b19569c3 (patch)
tree30de41b1c6b40669c82746f3c12daac54e62fdf9
initial commit
-rw-r--r--.gitignore4
-rw-r--r--Makefile36
-rw-r--r--package.yaml24
-rwxr-xr-xrepgoal.hs145
-rw-r--r--stack.yaml6
5 files changed, 215 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8e9be6a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
1.stack-work/
2bin
3rep-goal-calc.cabal
4stack.yaml.lock
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..92becce
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,36 @@
1stack_path != which stack
2
3all: $(if $(stack_path),,install-stack)
4 stack build
5 ln -sfT $$(stack path --local-install-root)/bin ./bin
6
7.PHONY: all run install install-dev-tools
8
9run: all
10 ./bin/repgoal
11
12install:
13 stack install
14
15install-dev-tools:
16 stack install apply-refact hlint stylish-haskell hasktags hoogle
17
18become_root != [ "$$(id -u)" = 0 ] || echo sudo
19install-stack:
20ifneq (,$(shell which curl))
21 curl -sSL https://get.haskellstack.org/ | sh
22else
23ifneq (,$(shell which wget))
24 wget -qO- https://get.haskellstack.org/ | sh
25else
26ifneq (,$(shell which apt))
27 $(become_root) apt install haskell-stack
28else
29 $(error No stack binary found; and no known means of installing (no curl, wget, or apt).\
30 Follow instructions at <https://haskellstack.org>)
31endif
32endif
33endif
34
35%.html: %.md
36 pandoc -f markdown -t html -o $@ $^
diff --git a/package.yaml b/package.yaml
new file mode 100644
index 0000000..8fe8bbc
--- /dev/null
+++ b/package.yaml
@@ -0,0 +1,24 @@
1name: rep-goal-calc
2version: 0.1.0.0
3
4dependencies:
5- base
6- rebase
7- lens
8- vty
9- time
10- mtl
11- data-default
12- containers
13- brick
14- format-numbers
15- text
16
17executables:
18 repgoal:
19 main: repgoal.hs
20 ghc-options:
21 - -threaded
22 - -rtsopts
23 - -with-rtsopts=-N
24
diff --git a/repgoal.hs b/repgoal.hs
new file mode 100755
index 0000000..b9c6df3
--- /dev/null
+++ b/repgoal.hs
@@ -0,0 +1,145 @@
1#!/usr/bin/env stack
2{- stack script --resolver lts-19.23 --install-ghc -}
3{-# OPTIONS_GHC
4 -Wall
5 -Wno-unused-imports
6 -Wno-unused-top-binds
7 -Wno-name-shadowing
8#-}
9{-# language NoImplicitPrelude #-}
10{-# language RecordWildCards #-}
11{-# language FlexibleContexts #-}
12{-# language TemplateHaskell #-}
13{-# language ViewPatterns #-}
14{-# language OverloadedStrings #-}
15import Rebase.Prelude hiding (toList, on, (<+>), Max)
16import qualified Rebase.Prelude as Prelude
17import Control.Lens hiding ((<|))
18import Data.Foldable (toList)
19import Data.Ratio
20import Text.Printf
21import Graphics.Vty
22import Data.Time.LocalTime
23import Control.Monad.RWS
24import Data.Time.Calendar.OrdinalDate
25import qualified Data.Text as Text
26import Data.Text.Format.Numbers
27import Rebase.Data.Map.Strict (Map)
28import qualified Rebase.Data.Map.Strict as Map
29
30import Brick
31import Brick.Types
32import Data.Text (unpack)
33import Control.Lens
34import Control.Monad (void, forever)
35import Control.Concurrent (threadDelay, forkIO)
36import qualified Graphics.Vty as V
37import Brick.Widgets.ProgressBar as P
38import Brick.BChan
39import Brick.Widgets.Center
40import Brick.Widgets.Border
41import Brick.Main
42 ( App(..)
43 , showFirstCursor
44 , customMain
45 , continue
46 , halt
47 )
48import Brick.AttrMap
49 ( attrMap
50 )
51import Brick.Types
52 ( Widget
53 , Next
54 , EventM
55 , BrickEvent(..)
56 )
57import Brick.Widgets.Core
58 ( (<=>)
59 , str
60 )
61import Brick.AttrMap as A
62import Brick.Util (fg, bg, on, clamp)
63import Brick.Widgets.Core
64import Brick.Widgets.Table
65
66data ExerciseStats = ExerciseStats {
67 exerciseName :: Text,
68 achievedReps :: Integer,
69 achievedWeight :: Rational
70}
71
72data ExerciseTarget = ExerciseTarget {
73 targetWeight :: Rational,
74 stats :: ExerciseStats
75}
76
77exercises :: [ExerciseTarget]
78exercises = [
79 ExerciseTarget 345 $ ExerciseStats "Deadlift" 9 315,
80 ExerciseTarget 130 $ ExerciseStats "Press" 9 120
81 ]
82
83computeRepGoal :: ExerciseTarget -> (Integer)
84computeRepGoal ExerciseTarget{..} = head $ filter isPR [2..]
85 where
86 isPR n = let goal1rm = computeOneRepMax $ stats { achievedReps = n, achievedWeight = targetWeight }
87 in goal1rm > computeOneRepMax stats
88
89-- The 1RM estimation formula from Jim Wendler's 5-3-1 Powerlifting
90-- manual:
91
92-- Estimated 1 RM = Weight x Reps x 0.0333 + Weight.
93
94computeOneRepMax :: ExerciseStats -> Rational
95computeOneRepMax ExerciseStats{..} = achievedWeight * (realToFrac achievedReps * 0.0333 + 1)
96
97showRational :: Rational -> String
98showRational = printf "%.3f" . (realToFrac :: Rational -> Float)
99
100drawUI :: () -> [Widget ()]
101drawUI () = [a]
102 where
103 a = hCenter $ renderTable $ table $
104 [str "Exercise", str "Achieved Reps", str "Computed 1RM", str "Weight", str "Goal Reps", str "Goal 1RM"] : map toRow exercises
105 toRow x@ExerciseTarget{..} =
106 let ExerciseStats{..} = stats
107 repGoal = computeRepGoal x
108 oneRepMaxGoal = computeOneRepMax $ ExerciseStats exerciseName repGoal targetWeight
109 in
110 [
111 txt exerciseName,
112 str $ printf "%d @ %s" achievedReps (showRational achievedWeight),
113 str $ showRational $ computeOneRepMax stats,
114 str $ showRational targetWeight,
115 str $ show repGoal,
116 str $ showRational oneRepMaxGoal
117 ]
118 where
119
120type St = ()
121
122type CustomEvent = ()
123
124handleEvent :: St -> BrickEvent () CustomEvent -> EventM () (Next St)
125handleEvent st e = case e of
126 VtyEvent (V.EvKey V.KEsc []) -> halt st
127 VtyEvent _ -> continue st
128 AppEvent _ -> continue st
129 _ -> continue st
130
131theApp :: App St CustomEvent ()
132theApp =
133 App { appDraw = drawUI
134 , appChooseCursor = showFirstCursor
135 , appHandleEvent = handleEvent
136 , appStartEvent = return
137 , appAttrMap = const $ A.attrMap V.defAttr []
138 }
139
140main :: IO ()
141main = do
142 let buildVty = V.mkVty V.defaultConfig
143 initialVty <- buildVty
144 chan <- newBChan 10
145 void $ customMain initialVty buildVty (Just chan) (theApp) ()
diff --git a/stack.yaml b/stack.yaml
new file mode 100644
index 0000000..86de4af
--- /dev/null
+++ b/stack.yaml
@@ -0,0 +1,6 @@
1resolver:
2 url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/19/23.yaml
3packages:
4- .
5compiler-check: newer-minor
6extra-deps: