|
@@ -0,0 +1,51 @@
|
|
|
|
+#!/usr/bin/env python3
|
|
|
|
+
|
|
|
|
+import sys
|
|
|
|
+import typing
|
|
|
|
+
|
|
|
|
+import pandas
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def _merge_adjacent(
|
|
|
|
+ recordings: pandas.DataFrame, remove_tags: typing.Set[str]
|
|
|
|
+) -> typing.Iterator[pandas.Series]:
|
|
|
|
+ previous_recording: typing.Optional[pandas.Series] = None
|
|
|
|
+ for _, next_recording in recordings.sort_values("start").iterrows():
|
|
|
|
+ next_recording["tags"] = set(next_recording["tags"]).difference(remove_tags)
|
|
|
|
+ if previous_recording is not None:
|
|
|
|
+ if next_recording["start"] <= previous_recording["end"]:
|
|
|
|
+ previous_recording["end"] = next_recording["end"]
|
|
|
|
+ previous_recording["tags"].update(next_recording["tags"])
|
|
|
|
+ else:
|
|
|
|
+ yield previous_recording
|
|
|
|
+ previous_recording = next_recording
|
|
|
|
+ else:
|
|
|
|
+ previous_recording = next_recording
|
|
|
|
+ if previous_recording is not None:
|
|
|
|
+ yield previous_recording
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def _main():
|
|
|
|
+ pandas.set_option("display.width", None)
|
|
|
|
+ pandas.set_option("display.max_rows", None)
|
|
|
|
+ # https://timewarrior.net/docs/api/
|
|
|
|
+ config_text, recordings_json = sys.stdin.read().split("\n\n", maxsplit=1)
|
|
|
|
+ config = dict(o.split(": ", maxsplit=1) for o in config_text.splitlines())
|
|
|
|
+ selected_tags = set(config["temp.report.tags"].split(","))
|
|
|
|
+ print(selected_tags)
|
|
|
|
+ recordings = pandas.DataFrame(
|
|
|
|
+ _merge_adjacent(
|
|
|
|
+ pandas.read_json(recordings_json, convert_dates=["start", "end"]).set_index(
|
|
|
|
+ "id", verify_integrity=True
|
|
|
|
+ ),
|
|
|
|
+ remove_tags=selected_tags,
|
|
|
|
+ )
|
|
|
|
+ ).assign(
|
|
|
|
+ start=lambda df: df["start"].dt.tz_convert("Europe/Vienna"),
|
|
|
|
+ end=lambda df: df["end"].dt.tz_convert("Europe/Vienna"),
|
|
|
|
+ )
|
|
|
|
+ print(recordings)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+if __name__ == "__main__":
|
|
|
|
+ _main()
|