From 1755f619c983df0beff5da377076d5bb890e8ce2 Mon Sep 17 00:00:00 2001 From: Sergey Magidovich Date: Thu, 31 Mar 2016 14:14:35 +0300 Subject: [PATCH] Simple mode can handle oh rules with off modifier. --- .../opening_hours_tests.cpp | 11 ++ editor/editor_tests/ui2oh_test.cpp | 114 +++++++++++++- editor/opening_hours_ui.cpp | 2 +- editor/ui2oh.cpp | 141 +++++++++++++++++- 4 files changed, 255 insertions(+), 13 deletions(-) diff --git a/3party/opening_hours/opening_hours_tests/opening_hours_tests.cpp b/3party/opening_hours/opening_hours_tests/opening_hours_tests.cpp index 8098a35a1e..3bef1f5c31 100644 --- a/3party/opening_hours/opening_hours_tests/opening_hours_tests.cpp +++ b/3party/opening_hours/opening_hours_tests/opening_hours_tests.cpp @@ -793,6 +793,11 @@ BOOST_AUTO_TEST_CASE(OpeningHoursWeekdays_TestParseUnparse) auto const parsedUnparsed = ParseAndUnparse(rule); BOOST_CHECK_EQUAL(parsedUnparsed, rule); } + { + auto const rule = "Sa"; + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } } BOOST_AUTO_TEST_CASE(OpeningHoursMonthdayRanges_TestParseUnparse) @@ -1063,6 +1068,12 @@ BOOST_AUTO_TEST_CASE(OpeningHoursRuleSequence_TestParseUnparse) { auto const rule = "Mo-Fr closed \"always closed\""; + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } + { + auto const rule = "Sa; Su"; + auto const parsedUnparsed = ParseAndUnparse(rule); BOOST_CHECK_EQUAL(parsedUnparsed, rule); } diff --git a/editor/editor_tests/ui2oh_test.cpp b/editor/editor_tests/ui2oh_test.cpp index d463ebf921..f11d75d63f 100644 --- a/editor/editor_tests/ui2oh_test.cpp +++ b/editor/editor_tests/ui2oh_test.cpp @@ -16,7 +16,7 @@ string ToString(OpeningHours const & oh) } } // namespace -UNIT_TEST(OpeningHours2TimeTableSt) +UNIT_TEST(OpeningHours2TimeTableSet) { { OpeningHours oh("08:00-22:00"); @@ -192,14 +192,120 @@ UNIT_TEST(OpeningHours2TimeTableSt) TEST_EQUAL(tt.GetOpeningDays().size(), 1, ()); } } +} + +UNIT_TEST(OpeningHours2TimeTableSet_off) +{ { - // We don't handle offs for for now. - OpeningHours oh("Mo-Fr 08:00-13:00,14:00-20:00; Sa 09:00-13:00,14:00-18:00; Su off"); + OpeningHours oh("Mo-Fr 08:00-13:00,14:00-20:00; Su off"); TEST(oh.IsValid(), ()); TimeTableSet tts; - TEST(!MakeTimeTableSet(oh, tts), ()); + TEST(MakeTimeTableSet(oh, tts), ()); + } + { + OpeningHours oh("Mo-Su 08:00-13:00,14:00-20:00; Sa off"); + TEST(oh.IsValid(), ()); + + TimeTableSet tts; + + TEST(MakeTimeTableSet(oh, tts), ()); + TEST_EQUAL(tts.GetUnhandledDays(), TOpeningDays({osmoh::Weekday::Saturday}), ()); + } + { + OpeningHours oh("Sa; Su; Sa off"); + TEST(oh.IsValid(), ()); + + TimeTableSet tts; + + TEST(MakeTimeTableSet(oh, tts), ()); + TEST_EQUAL(tts.GetUnhandledDays(), + TOpeningDays({osmoh::Weekday::Monday, + osmoh::Weekday::Tuesday, + osmoh::Weekday::Wednesday, + osmoh::Weekday::Thursday, + osmoh::Weekday::Friday, + osmoh::Weekday::Saturday}), ()); + } + { + OpeningHours oh("Mo-Su 08:00-13:00,14:00-20:00; Sa 10:00-11:00 off"); + TEST(oh.IsValid(), ()); + + TimeTableSet tts; + + TEST(MakeTimeTableSet(oh, tts), ()); + TEST_EQUAL(tts.Size(), 2, ()); + + auto const tt = tts.Get(1); + TEST_EQUAL(tt.GetOpeningDays(), TOpeningDays({osmoh::Weekday::Saturday}), ()); + TEST_EQUAL(tt.GetOpeningTime().GetStart().GetHourMinutes().GetHoursCount(), 8, ()); + TEST_EQUAL(tt.GetOpeningTime().GetEnd().GetHourMinutes().GetHoursCount(), 20, ()); + TEST_EQUAL(tt.GetExcludeTime()[0].GetStart().GetHourMinutes().GetHoursCount(), 10, ()); + TEST_EQUAL(tt.GetExcludeTime()[0].GetEnd().GetHourMinutes().GetHoursCount(), 11, ()); + TEST_EQUAL(tt.GetExcludeTime()[1].GetStart().GetHourMinutes().GetHoursCount(), 13, ()); + TEST_EQUAL(tt.GetExcludeTime()[1].GetEnd().GetHourMinutes().GetHoursCount(), 14, ()); + } + { + OpeningHours oh("Mo-Su; Sa 10:00-11:00 off"); + TEST(oh.IsValid(), ()); + + TimeTableSet tts; + + TEST(MakeTimeTableSet(oh, tts), ()); + TEST_EQUAL(tts.Size(), 2, ()); + + auto const tt = tts.Get(1); + + TEST_EQUAL(tt.GetOpeningTime().GetStart().GetHourMinutes().GetHoursCount(), 0, ()); + TEST_EQUAL(tt.GetOpeningTime().GetEnd().GetHourMinutes().GetHoursCount(), 24, ()); + + TEST_EQUAL(tt.GetOpeningDays(), TOpeningDays({osmoh::Weekday::Saturday}), ()); + TEST_EQUAL(tt.GetExcludeTime()[0].GetStart().GetHourMinutes().GetHoursCount(), 10, ()); + TEST_EQUAL(tt.GetExcludeTime()[0].GetEnd().GetHourMinutes().GetHoursCount(), 11, ()); + } + { + OpeningHours oh("Mo-Fr 11:00-17:00; Sa-Su 12:00-16:00; Tu off"); + TEST(oh.IsValid(), ()); + + TimeTableSet tts; + + TEST(MakeTimeTableSet(oh, tts), ()); + TEST_EQUAL(tts.Size(), 2, ()); + TEST_EQUAL(tts.GetUnhandledDays(), TOpeningDays({osmoh::Weekday::Tuesday}), ()); + } + { + OpeningHours oh("Mo-Fr 11:00-17:00; Sa-Su 12:00-16:00; Mo-Fr off"); + TEST(oh.IsValid(), ()); + + TimeTableSet tts; + + TEST(MakeTimeTableSet(oh, tts), ()); + TEST_EQUAL(tts.Size(), 1, ()); + + TEST_EQUAL(tts.GetUnhandledDays(), + TOpeningDays({osmoh::Weekday::Monday, + osmoh::Weekday::Tuesday, + osmoh::Weekday::Wednesday, + osmoh::Weekday::Thursday, + osmoh::Weekday::Friday}), ()); + } + { + OpeningHours oh("Mo-Fr 11:00-17:00; Sa-Su 12:00-16:00; Mo-Fr 11:00-13:00 off"); + TEST(oh.IsValid(), ()); + + TimeTableSet tts; + + TEST(MakeTimeTableSet(oh, tts), ()); + TEST_EQUAL(tts.Size(), 2, ()); + + auto const tt = tts.Get(0); + TEST_EQUAL(tts.GetUnhandledDays(), TOpeningDays(), ()); + + TEST_EQUAL(tt.GetOpeningTime().GetStart().GetHourMinutes().GetHoursCount(), 11, ()); + TEST_EQUAL(tt.GetOpeningTime().GetEnd().GetHourMinutes().GetHoursCount(), 17, ()); + TEST_EQUAL(tt.GetExcludeTime()[0].GetStart().GetHourMinutes().GetHoursCount(), 11, ()); + TEST_EQUAL(tt.GetExcludeTime()[0].GetEnd().GetHourMinutes().GetHoursCount(), 13, ()); } } diff --git a/editor/opening_hours_ui.cpp b/editor/opening_hours_ui.cpp index 0bc5223b6a..1334512cb8 100644 --- a/editor/opening_hours_ui.cpp +++ b/editor/opening_hours_ui.cpp @@ -338,7 +338,7 @@ bool TimeTableSet::Append(TimeTable const & tt) bool TimeTableSet::Remove(size_t const index) { - if (index == 0 || index >= Size()) + if (Size() == 1 || index >= Size()) return false; m_table.erase(m_table.begin() + index); diff --git a/editor/ui2oh.cpp b/editor/ui2oh.cpp index 179cc56f4b..19e0abfd79 100644 --- a/editor/ui2oh.cpp +++ b/editor/ui2oh.cpp @@ -10,7 +10,7 @@ namespace { -void SetUpWeekdays(osmoh::Weekdays const & wds, editor::ui::TimeTable & tt) +editor::ui::TOpeningDays MakeOpeningDays(osmoh::Weekdays const & wds) { set openingDays; for (auto const & wd : wds.GetWeekdayRanges()) @@ -30,7 +30,12 @@ void SetUpWeekdays(osmoh::Weekdays const & wds, editor::ui::TimeTable & tt) if (wd.HasSaturday()) openingDays.insert(osmoh::Weekday::Saturday); } - tt.SetOpeningDays(openingDays); + return openingDays; +} + +void SetUpWeekdays(osmoh::Weekdays const & wds, editor::ui::TimeTable & tt) +{ + tt.SetOpeningDays(MakeOpeningDays(wds)); } void SetUpTimeTable(osmoh::TTimespans spans, editor::ui::TimeTable & tt) @@ -164,6 +169,117 @@ editor::ui::TOpeningDays const kWholeWeek = { osmoh::Weekday::Saturday, osmoh::Weekday::Sunday }; + +editor::ui::TOpeningDays GetCommonDays(editor::ui::TOpeningDays const & a, + editor::ui::TOpeningDays const & b) +{ + editor::ui::TOpeningDays result; + set_intersection(begin(a), end(a), begin(b), end(b), inserter(result, begin(result))); + return result; +} + +osmoh::HourMinutes::TMinutes::rep GetDuration(osmoh::Time const & time) +{ + return time.GetHourMinutes().GetDurationCount(); +} + +bool Includes(osmoh::Timespan const & a, osmoh::Timespan const & b) +{ + return GetDuration(a.GetStart()) <= GetDuration(b.GetStart()) && + GetDuration(b.GetEnd()) <= GetDuration(a.GetEnd()); +} + +bool ExcludeRulePart(osmoh::RuleSequence const & rulePart, editor::ui::TimeTableSet & tts) +{ + auto const ttsInitialSize = tts.Size(); + for (size_t i = 0; i < ttsInitialSize; ++i) + { + auto tt = tts.Get(i); + auto const ttOpeningDays = tt.GetOpeningDays(); + auto const commonDays = GetCommonDays(ttOpeningDays, + MakeOpeningDays(rulePart.GetWeekdays())); + + auto const removeCommonDays = [&commonDays](editor::ui::TTimeTableProxy & tt) + { + for (auto const day : commonDays) + VERIFY(tt.RemoveWorkingDay(day), ("Can't remove working day")); + VERIFY(tt.Commit(), ("Can't commit changes")); + }; + + auto const twentyFourHoursGuard = [](editor::ui::TimeTable & tt) + { + using osmoh::operator ""_h; + if (tt.IsTwentyFourHours()) + { + tt.SetTwentyFourHours(false); + // TODO(mgsergio): Consider TimeTable refactoring: + // get rid of separation of TwentyFourHours and OpeningTime. + tt.SetOpeningTime({0_h, 24_h}); + } + }; + + auto const & excludeTime = rulePart.GetTimes(); + // The whole rule matches to the tt. + if (commonDays.size() == ttOpeningDays.size()) + { + // rulePart applies to commonDays in a whole. + if (excludeTime.empty()) + return tts.Remove(i); + + twentyFourHoursGuard(tt); + + for (auto const & time : excludeTime) + { + // Whatever it is, it's already closed at a time out of opening time. + if (!Includes(tt.GetOpeningTime(), time)) + continue; + + // The whole opening time interval should be switched off + if (!tt.AddExcludeTime(time)) + return tts.Remove(i); + } + VERIFY(tt.Commit(), ("Can't update time table")); + return true; + } + // A rule is applied to a subset of a time table. We should + // subtract common parts from tt and add a new time table if needed. + if (commonDays.size() != 0) + { + // rulePart applies to commonDays in a whole. + if (excludeTime.empty()) + { + removeCommonDays(tt); + continue; + } + + twentyFourHoursGuard(tt); + + editor::ui::TimeTable copy = tt; + VERIFY(copy.SetOpeningDays(commonDays), ("Can't set opening days")); + + auto doAppendRest = true; + for (auto const & time : excludeTime) + { + // Whatever it is, it's already closed at a time out of opening time. + if (!Includes(copy.GetOpeningTime(), time)) + continue; + + // The whole opening time interval should be switched off + if (!copy.AddExcludeTime(time)) + { + doAppendRest = false; + break; + } + } + + removeCommonDays(tt); + + if (doAppendRest) + VERIFY(tts.Append(copy), ("Can't add new time table")); + } + } + return true; +} } // namespace namespace editor @@ -209,14 +325,22 @@ bool MakeTimeTableSet(osmoh::OpeningHours const & oh, ui::TimeTableSet & tts) ui::TimeTable tt = ui::TimeTable::GetUninitializedTimeTable(); tt.SetOpeningTime(tt.GetPredefinedOpeningTime()); - // TODO(mgsergio): We don't handle cases with speciffic time off. - // I.e. Mo-Fr 08-20; Fr 18:00-17:00 off. - // Can be implemented later. - if (rulePart.GetModifier() == osmoh::RuleSequence::Modifier::Closed || - rulePart.GetModifier() == osmoh::RuleSequence::Modifier::Unknown || + // Comments and unknown rules belong to advanced mode. + if (rulePart.GetModifier() == osmoh::RuleSequence::Modifier::Unknown || rulePart.GetModifier() == osmoh::RuleSequence::Modifier::Comment) return false; + if (rulePart.GetModifier() == osmoh::RuleSequence::Modifier::Closed) + { + // off modifier in the first part in oh is useless. + if (first == true) + return false; + + if (!ExcludeRulePart(rulePart, tts)) + return false; + continue; + } + if (rulePart.HasWeekdays()) SetUpWeekdays(rulePart.GetWeekdays(), tt); else @@ -232,7 +356,8 @@ bool MakeTimeTableSet(osmoh::OpeningHours const & oh, ui::TimeTableSet & tts) tt.SetTwentyFourHours(true); } - bool const appended = first ? tts.Replace(tt, 0) : tts.Append(tt); + // Check size as well since ExcludeRulePart can add new time tables. + bool const appended = first && tts.Size() == 1 ? tts.Replace(tt, 0) : tts.Append(tt); first = false; if (!appended) return false;