Created
March 4, 2021 14:06
-
-
Save olimsaidov/ca2e6a140c2b8bf8ad81509484c1fe37 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(require '[babashka.curl :as curl]) | |
(require '[cheshire.core :as json]) | |
(require '[clojure.java.io :as io]) | |
(require '[clojure.string :as str]) | |
(import 'java.time.YearMonth) | |
(def bearer-token | |
"eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIyMjhUUUQiLCJzdWIiOiI0VkNOVjYiLCJ4YWkiOiI1NTExNTU4OTU5MzIiLCJ4dmVyIjoiMCIsImlzcyI6IkZpdGJpdCIsInR5cCI6ImFjY2Vzc190b2tlbiIsInNjb3BlcyI6IndociB3bnV0IHdwcm8gd3NsZSB3d2VpIHdtZmEgd3NvYyB3YWN0IHdzZXQgd2xvYyIsImV4cCI6MTYxNTAwNjg3NCwiaWF0IjoxNjE0ODM0MDc0fQ.FdaLszvp6vS4K5kUQ06ylOF_25y_rE0AbsFuMIke4tc") | |
(defn request-export | |
[start-date end-date] | |
(-> (curl/post "https://web-api.fitbit.com/1/user/-/legacy/export/request-export.json" | |
{:headers {"Authorization" (str "Bearer " bearer-token)} | |
:body (str "periodType=CUSTOM&dataTypes=BODY%2CFOODS%2CACTIVITIES%2CSLEEP&dataExportFileFormat=CSV&startDate=" start-date "&endDate=" end-date)}) | |
(:body) | |
(json/parse-string true) | |
(:fileIdentifier))) | |
(def ranges | |
(->> (for [year (range 2015 2021)] | |
(for [month (range 1 13)] | |
[year month])) | |
(apply concat) | |
(map (fn [[year month]] | |
[(str (.atDay (YearMonth/of year month) 1)) | |
(str (.atEndOfMonth (YearMonth/of year month)))])))) | |
(def file-identifiers | |
(->> ranges | |
(pmap (partial apply request-export)))) | |
(defn export-status | |
[file-identifier] | |
(-> (curl/get "https://web-api.fitbit.com/1/user/-/legacy/export/export-status.json" | |
{:headers {"Authorization" (str "Bearer " bearer-token)} | |
:query-params {"fileIdentifier" file-identifier}}) | |
(:body) | |
(json/parse-string true) | |
(:fileIsReady))) | |
(defn wait-exports | |
[file-identifiers] | |
(loop [file-identifiers file-identifiers] | |
(if (empty? file-identifiers) | |
:ready | |
(do (Thread/sleep 500) | |
(recur (->> file-identifiers | |
(pmap #(vector % (export-status %))) | |
(keep #(when-not (second %) (first %))))))))) | |
(wait-exports file-identifiers) | |
(defn get-completed-export | |
[file-identifier] | |
(-> (curl/get "https://web-api.fitbit.com/1/user/-/legacy/export/get-completed-export.json" | |
{:headers {"Authorization" (str "Bearer " bearer-token)} | |
:query-params {"fileIdentifier" file-identifier}}) | |
(:body) | |
(json/parse-string true) | |
(:exportUrl))) | |
(def urls | |
(->> file-identifiers | |
(pmap get-completed-export))) | |
(defn download-urls | |
[output names urls] | |
(->> (map vector names urls) | |
(pmap (fn [[name url]] | |
(io/copy | |
(:body (curl/get url {:as :stream})) | |
(io/file output name)))) | |
(doall))) | |
(download-urls "output" (map (fn [[s e]] (str s "-" e ".csv")) ranges) urls) | |
(->> (file-seq (io/file "output")) | |
(filter #(str/ends-with? (str %) ".csv")) | |
(map slurp) | |
(map str/split-lines) | |
(map #(take-while #(not= "Foods" %) %)) | |
(map drop-last) | |
(map #(drop 2 %)) | |
(apply concat) | |
(sort) | |
(dedupe) | |
(str/join "\n") | |
(spit "unified.csv")) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment