diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 44ed543d..825cab4a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ set (timew_SRCS AtomicFile.cpp AtomicFile.h Exclusion.cpp Exclusion.h Extensions.cpp Extensions.h ExtensionsTable.cpp ExtensionsTable.h + GapsTable.cpp GapsTable.h Interval.cpp Interval.h IntervalFactory.cpp IntervalFactory.h IntervalFilter.cpp IntervalFilter.h diff --git a/src/GapsTable.cpp b/src/GapsTable.cpp new file mode 100644 index 00000000..2c3c9c78 --- /dev/null +++ b/src/GapsTable.cpp @@ -0,0 +1,114 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2023, Gothenburg Bit Factory. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// https://www.opensource.org/licenses/mit-license.php +// +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +GapsTable::Builder GapsTable::builder () +{ + return {}; +} + +/////////////////////////////////////////////////////////////////////////////// +GapsTable::Builder& GapsTable::Builder::withRange (const Range &range) +{ + _range = range; + return *this; +} + +/////////////////////////////////////////////////////////////////////////////// +GapsTable::Builder& GapsTable::Builder::withIntervals (const std::vector &intervals) +{ + _intervals = intervals; + return *this; +} + +/////////////////////////////////////////////////////////////////////////////// +Table GapsTable::Builder::build () +{ + Table table; + table.width (1024); + table.colorHeader (Color ("underline")); + table.add ("Wk"); + table.add ("Date"); + table.add ("Day"); + table.add ("Start", false); + table.add ("End", false); + table.add ("Time", false); + table.add ("Total", false); + + // Each day is rendered separately. + time_t grand_total = 0; + Datetime previous; + for (Datetime day = _range.start; day < _range.end; day++) + { + auto day_range = getFullDay (day); + time_t daily_total = 0; + + int row = -1; + for (auto &gap: subset (day_range, _intervals)) + { + row = table.addRow (); + + if (day != previous) + { + table.set (row, 0, format ("W{1}", day.week ())); + table.set (row, 1, day.toString ("Y-M-D")); + table.set (row, 2, Datetime::dayNameShort (day.dayOfWeek ())); + previous = day; + } + + // Intersect track with day. + auto today = day_range.intersect (gap); + if (gap.is_open ()) + { + today.end = Datetime (); + } + + table.set (row, 3, today.start.toString ("h:N:S")); + table.set (row, 4, (gap.is_open () ? "-" : today.end.toString ("h:N:S"))); + table.set (row, 5, Duration (today.total ()).formatHours ()); + + daily_total += today.total (); + } + + if (row != -1) + { + table.set (row, 6, Duration (daily_total).formatHours ()); + } + + grand_total += daily_total; + } + + // Add the total. + table.set (table.addRow (), 6, " ", Color ("underline")); + table.set (table.addRow (), 6, Duration (grand_total).formatHours ()); + + return table; +} diff --git a/src/GapsTable.h b/src/GapsTable.h new file mode 100644 index 00000000..b8ae36f7 --- /dev/null +++ b/src/GapsTable.h @@ -0,0 +1,54 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2023, Gothenburg Bit Factory. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// https://www.opensource.org/licenses/mit-license.php +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDED_GAPSTABLE +#define INCLUDED_GAPSTABLE + +#include +#include +#include +#include + +class GapsTable +{ + class Builder + { + public: + Builder& withRange (const Range &); + Builder& withIntervals (const std::vector &); + + Table build (); + + private: + std::vector _intervals; + Range _range; + }; + +public: + static Builder builder (); +}; + +#endif //INCLUDED_GAPSTABLE diff --git a/src/commands/CmdGaps.cpp b/src/commands/CmdGaps.cpp index 37dbb5bb..4234f4cb 100644 --- a/src/commands/CmdGaps.cpp +++ b/src/commands/CmdGaps.cpp @@ -1,6 +1,6 @@ //////////////////////////////////////////////////////////////////////////////// // -// Copyright 2016 - 2023, Thomas Lauf, Paul Beckingham, Federico Hernandez. +// Copyright 2016 - 2023, Gothenburg Bit Factory. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -25,9 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include -#include #include #include @@ -62,70 +61,23 @@ int CmdGaps ( untracked = getUntracked (database, rules, filter); } - Table table; - table.width (1024); - table.colorHeader (Color ("underline")); - table.add ("Wk"); - table.add ("Date"); - table.add ("Day"); - table.add ("Start", false); - table.add ("End", false); - table.add ("Time", false); - table.add ("Total", false); - - // Each day is rendered separately. - time_t grand_total = 0; - Datetime previous; - for (Datetime day = range.start; day < range.end; day++) + if (untracked.empty ()) { - auto day_range = getFullDay (day); - time_t daily_total = 0; - - int row = -1; - for (auto& gap : subset (day_range, untracked)) + if (verbose) { - row = table.addRow (); - - if (day != previous) - { - table.set (row, 0, format ("W{1}", day.week ())); - table.set (row, 1, day.toString ("Y-M-D")); - table.set (row, 2, Datetime::dayNameShort (day.dayOfWeek ())); - previous = day; - } - - // Intersect track with day. - auto today = day_range.intersect (gap); - if (gap.is_open ()) - today.end = Datetime (); - - table.set (row, 3, today.start.toString ("h:N:S")); - table.set (row, 4, (gap.is_open () ? "-" : today.end.toString ("h:N:S"))); - table.set (row, 5, Duration (today.total ()).formatHours ()); - - daily_total += today.total (); + std::cout << "No gaps found.\n"; } - - if (row != -1) - table.set (row, 6, Duration (daily_total).formatHours ()); - - grand_total += daily_total; - } - - // Add the total. - table.set (table.addRow (), 6, " ", Color ("underline")); - table.set (table.addRow (), 6, Duration (grand_total).formatHours ()); - - if (table.rows () > 2) - { - std::cout << '\n' - << table.render () - << '\n'; } else { - if (verbose) - std::cout << "No gaps found.\n"; + auto table = GapsTable::builder () + .withRange (range) + .withIntervals (untracked) + .build (); + + std::cout << '\n' + << table.render () + << '\n'; } return 0;