Skip to content

Commit

Permalink
Add basic sugarwod import for lifts (#81)
Browse files Browse the repository at this point in the history
* add basic sugarwod import for lifts

* remove middleware

* remove reference to admin

* add snatch grip pp and snatch pull to mvmt list

---------

Co-authored-by: Zac Jones <[email protected]>
  • Loading branch information
theianjones and zacjones93 authored Oct 23, 2023
1 parent 24270fb commit 8899d47
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 8 deletions.
16 changes: 10 additions & 6 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{:paths ["src" "resources" "target/resources"]
:deps {com.biffweb/biff {:git/url "https://github.com/jacobobryant/biff", :sha "3ff1256", :tag "v0.7.4"}
camel-snake-kebab/camel-snake-kebab {:mvn/version "0.4.3"}
org.clojure/clojure {:mvn/version "1.11.1"}
org.slf4j/slf4j-simple {:mvn/version "2.0.0-alpha5"}
clojure.java-time/clojure.java-time {:mvn/version "1.3.0"}
djblue/portal {:mvn/version "0.40.0"}}}
:deps {com.biffweb/biff {:git/url "https://github.com/jacobobryant/biff", :sha "3ff1256", :tag "v0.7.4"}
camel-snake-kebab/camel-snake-kebab {:mvn/version "0.4.3"}
net.clojars.wkok/openai-clojure {:mvn/version "0.11.0"}
cheshire/cheshire {:mvn/version "5.12.0"}
metosin/malli {:mvn/version "0.13.0"}
org.clojure/clojure {:mvn/version "1.11.1"}
org.clojure/data.csv {:mvn/version "1.0.1"}
org.slf4j/slf4j-simple {:mvn/version "2.0.0-alpha5"}
clojure.java-time/clojure.java-time {:mvn/version "1.3.0"}
djblue/portal {:mvn/version "0.40.0"}}}
28 changes: 28 additions & 0 deletions src/com/spicy/app.clj
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
(ns com.spicy.app
(:require
[clojure.java.io :as io]
[com.biffweb :as biff :refer [q]]
[com.spicy.middleware :as mid]
[com.spicy.movements.core :as movements]
[com.spicy.results.core :as results]
[com.spicy.results.ui :as r]
[com.spicy.settings :as settings]
[com.spicy.sugarwod.core :as sugar.core]
[com.spicy.sugarwod.csv :as csv]
[com.spicy.ui :as ui]
[com.spicy.workouts.core :as workouts]
[com.spicy.workouts.ui :as w]))
Expand Down Expand Up @@ -80,10 +83,35 @@
[:a.link {:href "https://biffweb.com"} "Biff"] "."]))


(defn import-page
[ctx]
(ui/page
ctx
(ui/panel [:h1.text-2xl.font-bold.mb-8
"Import your data"]
[:h2.text-xl.font-bold.mb-4
"SugarWOD"]
(biff/form {:method :post :enctype "multipart/form-data"}
[:input {:type :file :name :file :id :file}]
[:button.btn {:type "submit"} "Submit"]))))


(defn process-import
[{:keys [params session] :as ctx}]
(let [user (:uid session)
data (csv/parse-sugar-csv (io/reader (-> params :file :tempfile)))
tx-data (sugar.core/sugar-movements->tx-data (assoc ctx :user user) data)]
(biff/submit-tx ctx tx-data))
{:status 303
:headers {"location" "/app/import"}})


(def plugin
{:static {"/about/" about-page}
:routes ["/app" {:middleware [mid/wrap-signed-in]}
["" {:get app}]
["/import" {:get import-page
:post process-import}]
workouts/routes
results/routes
movements/routes]
Expand Down
5 changes: 3 additions & 2 deletions src/com/spicy/numbers.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

(defn parse-int
[s]
(Integer/parseInt (re-find #"\A-?\d+" s)))
(if (int? s)
s
(Integer/parseInt (re-find #"\A-?\d+" s))))


(defn safe-parse-int
Expand All @@ -12,4 +14,3 @@
(parse-int s)
(catch Exception e
(prn "Error while parsing int: " e))))

56 changes: 56 additions & 0 deletions src/com/spicy/sugarwod/core.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
(ns com.spicy.sugarwod.core
(:require
[com.biffweb :as biff]
[com.spicy.sugarwod.transform :as t]))


(defn sugar-lift?
[{:keys [barbell-lift] :as _sugar-record}]
(not-empty barbell-lift))


(defn sugar-movements->tx-data
[{:keys [user] :as ctx} sugarwod-csv-data]
(let [spicy-lifts (biff/q (:biff/db ctx)
'{:find (pull m [*])
:where [[m :movement/name]
[m :movement/type :strength]]})
sugar-lifts (filter sugar-lift? sugarwod-csv-data)]
(filterv seq (flatten
(map
(partial t/transform-sugar-strength->tx {:user user :movements spicy-lifts})
sugar-lifts)))))


(comment
(require '[com.spicy.portal :as p])
(require '[com.spicy.repl :as r])
(require '[com.spicy.sugarwod.csv :as s])
(require '[clojure.java.io :as io])

(p/open-portal)

(s/decode-record {:description "Hang Power Clean for load: #1: 8 reps #2: 8 reps #3: 8 reps #4: 8 reps #5: 8 reps #6: 8 reps"
:date "08/12/2017"
:score-type "Load"
:set-details "[{\"success\":true,\"load\":115},{\"success\":true,\"load\":115},{\"success\":true,\"load\":115},{\"success\":true,\"load\":125},{\"success\":true,\"load\":125},{\"success\":true,\"load\":125}]"
:barbell-lift "Hang Power Clean"
:best-result-raw "125"
:title "Hang Power Clean 6x8"
:rx-or-scaled "RX"
:notes "Barbell Cycling, trying to bounce the bar out of the power position." :best-result-display "125"
:pr ""})

(def e (io/reader (io/resource "sugarwod_workouts.csv")))

(def data
(s/parse-sugar-csv e))

(tap> data)


(let [ctx (r/get-context)]
(tap>
(sugar-movements->tx-data (assoc ctx :user r/user-b) data)))

(biff/add-libs))
45 changes: 45 additions & 0 deletions src/com/spicy/sugarwod/csv.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
(ns com.spicy.sugarwod.csv
(:require
[camel-snake-kebab.core :as csk]
[cheshire.core :as json]
[clojure.data.csv :as csv]))


(def BarbellLiftEnums
[:enum "" "Shoulder Press" "Bench Press" "Split Jerk" "Push Press" "Sotts Press" "Clean & Jerk" "Power Clean" "Hang Power Snatch" "Pendlay Row" "Good Morning" "Clean Pull" "Deadlift" "Romanian Deadlift" "Squat Clean Thruster" "Back Pause Squat" "Hang Squat Clean" "Back Squat" "Thruster" "Sumo Deadlift" "Squat Snatch" "Box Squat" "Power Snatch" "Front Squat" "Muscle Snatch" "Power Clean & Jerk" "Snatch" "Hang Squat Snatch" "Muscle Clean" "Overhead Squat" "Hang Clean" "Hang Power Clean" "Squat Clean" "Push Jerk" "Front Pause Squat" "Clean"])


(def SugarRecord
[:map
[[:barbell-lift BarbellLiftEnums]
[:best-result-display :int]
[:best-result-raw :int]
[:date inst?]
[:description :string]
[:notes :string]
[:pr [:enum "PR"]]
[:rx-or-scaled [:enum "RX" "SCALED"]]
[:score-type [:enum "Load" "Rounds + Reps" "Other / Text" "Reps" "Time"]]
[:set-details :string]
[:title :string]]])


(defn csv-data->maps
[csv-data]
(map zipmap
(->> (first csv-data) ; First row is the header
(map csk/->kebab-case-keyword) ; Drop if you want string keys instead
repeat)
(rest csv-data)))


(defn decode-record
[r]
(assoc r :set-details (json/decode (:set-details r) keyword)))


(defn parse-sugar-csv
[csv]
(->> (csv/read-csv csv)
csv-data->maps
(map decode-record)))
144 changes: 144 additions & 0 deletions src/com/spicy/sugarwod/transform.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
(ns com.spicy.sugarwod.transform
(:require
[clojure.string :as string]
[com.spicy.calendar :as c]
[com.spicy.numbers :as n]
[java-time.api :as jt]))


(defn transformer
"Takes a translation map data to transform"
[transform-map raw-data]
(reduce-kv (fn [m k v]
(let [resolved
(cond
(vector? v) (if (fn? (last v))
((last v) (get-in raw-data (butlast v)))
(get-in raw-data v))
(fn? v) (v raw-data)
(map? v) (transformer v raw-data)
:else v)]
(assoc m k resolved)))
{}
transform-map))


(def sugar-lift->spicy-lift
{"Shoulder Press" "strict press"
"Bench Press" "bench press"
"Push Jerk" "push jerk"
"Split Jerk" "split jerk"
"Push Press" "push press"
"Sotts Press" "sotts press"
"Clean & Jerk" "clean and jerk"
"Power Clean" "power clean"
"Power Clean & Jerk" "power clean and jerk"
"Clean" "clean"
"Muscle Clean" "muscle clean"
"Hang Clean" "hang clean"
"Hang Power Clean" "hang power clean" "Clean Pull" "clean pull"
"Hang Squat Clean" "hang squat clean"
"Squat Clean" "squat clean"
"Squat Clean Thruster" "squat clean thruster (cluster)"
"Thruster" "thruster"
"Snatch" "snatch"
"Hang Power Snatch" "hang power snatch"
"Snatch Grip Push Press" "snatch grip push press"
"Snatch Pull" "snatch pull"
"Squat Snatch" "squat snatch"
"Power Snatch" "power snatch"
"Muscle Snatch" "muscle snatch"
"Hang Squat Snatch" "hang squat snatch"
"Pendlay Row" "pendlay row"
"Deadlift" "deadlift"
"Romanian Deadlift" "romainian deadlift"
"Sumo Deadlift" "sumo deadlift"
"Good Morning" "good morning"
"Back Squat" "back squat"
"Back Pause Squat" "back pause squat"
"Overhead Squat" "overhead squat"
"Box Squat" "box squat"
"Front Squat" "front squat"
"Front Pause Squat" "front pause squat"
})


(defn sugar-lift->xt-id
[movements lift-str]
(:xt/id (first (filter #(= (get sugar-lift->spicy-lift lift-str) (:movement/name %)) movements))))


(defmulti title->reps
(fn [title]
(cond
(seq (re-find #"^(\D+)\s+(\d+x\d+)+$" title)) :constant
(seq (re-find #"^(\D+)\s+((?:\d+-)+\d+)$" title)) :variable)))


(defmethod title->reps :constant
[title]
(when-let [[_ _ reps-str] (re-find #"^(\D+)\s+(\d+x\d+)+$" title)]
(let [[sets reps] (string/split reps-str #"x")]
(repeat (n/parse-int sets) (n/parse-int reps)))))


(defmethod title->reps :variable
[title]
(when-let [[_ _ reps-str] (re-find #"^(\D+)\s+((?:\d+-)+\d+)$" title)]
(let [reps (string/split reps-str #"-")]
(map n/parse-int reps))))


(defn ->instant
[date-str]
(->> date-str
(jt/local-date "MM/dd/yyyy")
c/->instant
java.util.Date/from))


(def sugar-set->spicy-set
{:db/doc-type :strength-set
:result-set/status [:success (fn [success?] (if success? :pass :fail))]
:result-set/weight [:load n/parse-int]})


(defn map-set-details
[{:keys [type-id] :as _opts}]
(fn [{:keys [set-details title] :as _sugar-record}]
(let [reps (title->reps title)
map-fn (fn [idx set]
(-> (transformer sugar-set->spicy-set set)
(assoc :result-set/number (inc idx)
:result-set/parent type-id
:db/op :create
:result-set/reps (nth reps idx))))]
(into [] (map-indexed map-fn set-details)))))


(defn sugar-strength->spicy-strength
[{:keys [type-id user movements] :as opts}]
{:strength-result {:result/movement [:barbell-lift (partial sugar-lift->xt-id movements)]
:result/notes [:notes]
:result/set-count [:set-details count]
:db/doc-type :strength-result
:db/op :create
:xt/id type-id}
:result {:result/date [:date ->instant]
:db/doc-type :result
:db/op :create
:result/type type-id
:result/user user}
:sets (map-set-details opts)})


(defn transform-sugar-strength->tx
[{:keys [_user _movements] :as ctx} sugar-record]
(try
(let [{:keys [strength-result result sets]}
(transformer
(sugar-strength->spicy-strength (assoc ctx :type-id (random-uuid)))
sugar-record)]
(concat [result strength-result] sets))
(catch Exception _e
nil)))

0 comments on commit 8899d47

Please sign in to comment.