diff --git a/src/commands/CmdContinue.cpp b/src/commands/CmdContinue.cpp index 1c204e6f..1672c9b4 100644 --- a/src/commands/CmdContinue.cpp +++ b/src/commands/CmdContinue.cpp @@ -52,7 +52,6 @@ int CmdContinue ( flattenDatabase (database, rules); Interval to_copy; - Interval latest = getLatestInterval (database); if (ids.size() == 1) { @@ -68,6 +67,8 @@ int CmdContinue ( } else { + Interval latest = getLatestInterval (database); + if (latest.empty ()) { throw std::string ("There is no previous tracking to continue."); @@ -100,17 +101,6 @@ int CmdContinue ( to_copy.start = start_time; to_copy.end = end_time; - if (latest.is_open ()) - { - Interval modified {latest}; - modified.end = start_time; - database.modifyInterval(latest, modified, verbose); - if (verbose) - { - std::cout << '\n' << intervalSummarize (database, rules, modified); - } - } - validate (cli, rules, database, to_copy); database.addInterval (to_copy, verbose); diff --git a/src/validate.cpp b/src/validate.cpp index a5c91eae..8c24cc65 100644 --- a/src/validate.cpp +++ b/src/validate.cpp @@ -112,8 +112,31 @@ static void autoAdjust ( debug (" " + overlap.dump ()); } + auto verbose = rules.getBoolean ("verbose"); + if (! adjust) { + // standard overlap resolution: an open interval can truncate another open interval + if (overlaps.size () == 1) + { + auto overlap = overlaps[0]; + + if (overlap.is_open () && interval.is_open () && interval.startsWithin (overlap)) + { + // start date of new interval within old interval + Interval modified {overlap}; + modified.end = interval.start; + database.modifyInterval (overlap, modified, verbose); + + if (verbose) + { + std::cout << '\n' << intervalSummarize (database, rules, modified); + } + + return; + } + } + throw std::string("You cannot overlap intervals. Correct the start/end " "time, or specify the :adjust hint."); } @@ -130,14 +153,14 @@ static void autoAdjust ( // start date of new interval within old interval Interval modified {overlap}; modified.end = interval.start; - database.modifyInterval (overlap, modified, rules.getBoolean ("verbose")); + database.modifyInterval (overlap, modified, verbose); } else if (!start_within_overlap && end_within_overlap) { // end date of new interval within old interval Interval modified {overlap}; modified.start = interval.end; - database.modifyInterval (overlap, modified, rules.getBoolean ("verbose")); + database.modifyInterval (overlap, modified, verbose); } else if (!start_within_overlap && !end_within_overlap) { @@ -159,12 +182,12 @@ static void autoAdjust ( } else { - database.modifyInterval (overlap, split1, rules.getBoolean ("verbose")); + database.modifyInterval (overlap, split1, verbose); } if (! split2.is_empty ()) { - database.addInterval (split2, rules.getBoolean ("verbose")); + database.addInterval (split2, verbose); } } } diff --git a/test/continue.t b/test/continue.t index 6292f08f..a6e00be1 100755 --- a/test/continue.t +++ b/test/continue.t @@ -150,6 +150,52 @@ class TestContinue(TestCase): expectedTags=["FOO"], description="continued interval") + def test_continue_without_adjust_hint(self): + """Verify that continuing without the :adjust hint fails to overwrite""" + now_utc = datetime.now().utcnow() + + two_hours_before_utc = now_utc - timedelta(hours=2) + three_hours_before_utc = now_utc - timedelta(hours=3) + four_hours_before_utc = now_utc - timedelta(hours=4) + five_hours_before_utc = now_utc - timedelta(hours=5) + + self.t("track FOO {:%Y-%m-%dT%H}:00:00Z - {:%Y-%m-%dT%H}:00:00Z".format(five_hours_before_utc, four_hours_before_utc)) + self.t("track BAR {:%Y-%m-%dT%H}:00:00Z - {:%Y-%m-%dT%H}:00:00Z".format(four_hours_before_utc, two_hours_before_utc)) + + self.t.runError("continue @2 {:%Y-%m-%dT%H}:00:00Z".format(three_hours_before_utc)) + + def test_continue_with_adjust_hint(self): + """Verify that continuing with the :adjust hint works""" + now_utc = datetime.now().utcnow() + + two_hours_before_utc = now_utc - timedelta(hours=2) + three_hours_before_utc = now_utc - timedelta(hours=3) + four_hours_before_utc = now_utc - timedelta(hours=4) + five_hours_before_utc = now_utc - timedelta(hours=5) + + self.t("track FOO {:%Y-%m-%dT%H}:00:00Z - {:%Y-%m-%dT%H}:00:00Z".format(five_hours_before_utc, four_hours_before_utc)) + self.t("track BAR {:%Y-%m-%dT%H}:00:00Z - {:%Y-%m-%dT%H}:00:00Z".format(four_hours_before_utc, two_hours_before_utc)) + + self.t("continue @2 {:%Y-%m-%dT%H}:00:00Z :adjust".format(three_hours_before_utc)) + + j = self.t.export() + + self.assertEqual(len(j), 3) + self.assertClosedInterval(j[0], + expectedStart="{:%Y%m%dT%H}0000Z".format(five_hours_before_utc), + expectedEnd="{:%Y%m%dT%H}0000Z".format(four_hours_before_utc), + expectedTags=["FOO"], + description="first interval") + self.assertClosedInterval(j[1], + expectedStart="{:%Y%m%dT%H}0000Z".format(four_hours_before_utc), + expectedEnd="{:%Y%m%dT%H}0000Z".format(three_hours_before_utc), + expectedTags=["BAR"], + description="second interval") + self.assertOpenInterval(j[2], + expectedStart="{:%Y%m%dT%H}0000Z".format(three_hours_before_utc), + expectedTags=["FOO"], + description="continued interval") + def test_continue_with_id_and_range(self): """Verify that continue with a range adds a copy with same tags""" now_utc = datetime.now().utcnow()