59 lines
1.4 KiB
D
59 lines
1.4 KiB
D
|
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;
|
||
|
}
|