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; }