diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..677485a --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +.dub +docs.json +__dummy.html +docs/ +/fitbit-extract +fitbit-extract.so +fitbit-extract.dylib +fitbit-extract.dll +fitbit-extract.a +fitbit-extract.lib +fitbit-extract-test-* +*.exe +*.pdb +*.o +*.obj +*.lst diff --git a/dub.json b/dub.json new file mode 100644 index 0000000..9949b77 --- /dev/null +++ b/dub.json @@ -0,0 +1,9 @@ +{ + "authors": [ + "Andrew Lalis" + ], + "copyright": "Copyright © 2024, Andrew Lalis", + "description": "Tools for parsing and extracting data from your FitBit export.", + "license": "MIT", + "name": "fitbit-extract" +} \ No newline at end of file diff --git a/source/app.d b/source/app.d new file mode 100644 index 0000000..1369ee0 --- /dev/null +++ b/source/app.d @@ -0,0 +1,58 @@ +import std.stdio; +import std.json; +import std.algorithm; +import std.array; +import std.csv; +import std.datetime; +import std.path; +import std.file; +import std.format; + +int main(string[] args) { + if (args.length < 2) { + stderr.writeln("Missing required first argument for FitBit directory."); + return 1; + } + string fitbitDir = args[1]; + auto w = parseWeight(fitbitDir); + writeln("Date, Weight"); + foreach (e; w) { + writefln!"%s, %0.1f"(e.date.toISOExtString, e.weight); + } + return 0; +} + +struct WeightEntry { + Date date; + float weight; +} + +WeightEntry[] parseWeight(string fitbitDir) { + string exportDataDir = buildPath(fitbitDir, "Global Export Data"); + Appender!(WeightEntry[]) app; + foreach (DirEntry entry; dirEntries(exportDataDir, SpanMode.shallow, false)) { + if (startsWith(baseName(entry.name), "weight")) { + JSONValue weightArray = parseJSON(cast(string) std.file.read(entry.name)); + foreach (JSONValue weightObj; weightArray.array) { + WeightEntry w; + w.weight = weightObj.object["weight"].floating; + int day, month, year; + formattedRead!"%d/%d/%d"(weightObj.object["date"].str, month, day, year); + w.date = Date(year + 2000, month, day); + + bool dateAlreadyExists = false; + foreach (WeightEntry existingEntry; app[]) { + if (existingEntry.date == w.date) { + dateAlreadyExists = true; + break; + } + } + + if (!dateAlreadyExists) app ~= w; + } + } + } + return app[] + .sort!((a, b) => a.date < b.date) + .array; +}