From 39855f800675efb4a9a31677620154fbe4592f28 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Thu, 28 Apr 2016 20:24:19 -0400 Subject: [PATCH] data: Migrated and renamed funcitons --- src/data.cpp | 144 ++++++++++++++--- src/helper.cpp | 430 +++---------------------------------------------- src/timew.h | 32 ++-- 3 files changed, 167 insertions(+), 439 deletions(-) diff --git a/src/data.cpp b/src/data.cpp index f8c1165f..186f7a07 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -29,6 +29,7 @@ #include #include #include +#include // TODO Remove. //////////////////////////////////////////////////////////////////////////////// // A filter is just another interval, containing start, end and tags. @@ -192,6 +193,8 @@ Interval getFilter (const CLI& cli) } filter.range = range; + std::cout << "# getFilter:\n"; + std::cout << "# " << filter.dump () << "\n"; return filter; } @@ -215,6 +218,9 @@ std::vector getHolidays (const Rules& rules) } } + std::cout << "# getHolidays:\n"; + for (auto& h : results) + std::cout << "# " << h.dump () << "\n"; return results; } @@ -231,8 +237,8 @@ std::vector getHolidays (const Rules& rules) // The result is the complete set of untrackable time that lies within the // input range. This will be a set of nights, weekends, holidays and lunchtimes. std::vector getAllExclusions ( - const Range& range, - const Rules& rules) + const Rules& rules, + const Range& range) { // Start with the set of all holidays, intersected with range. std::vector results; @@ -271,7 +277,12 @@ std::vector getAllExclusions ( for (auto& r : exclusion.ranges (range)) exclusionRanges.push_back (r); - return addRanges (range, results, exclusionRanges); + auto all = addRanges (range, results, exclusionRanges); + std::cout << "# getAllExclusions:\n"; + for (auto& r : all) + std::cout << "# " << r.dump () << "\n"; + return all; +// return addRanges (range, results, exclusionRanges); } //////////////////////////////////////////////////////////////////////////////// @@ -282,11 +293,14 @@ std::vector getExclusions (const Rules& rules) for (auto& name : rules.all ("exclusions.")) all.push_back (Exclusion (lowerCase (name), rules.get (name))); + std::cout << "# getExclusions:\n"; + for (auto& e : all) + std::cout << "# " << e.dump () << "\n"; return all; } //////////////////////////////////////////////////////////////////////////////// -std::vector getInclusions (Database& database) +std::vector getAllInclusions (Database& database) { std::vector all; for (auto& line : database.allLines ()) @@ -296,6 +310,9 @@ std::vector getInclusions (Database& database) all.push_back (i); } + std::cout << "# getAllInclusions:\n"; + for (auto& i : all) + std::cout << "# " << i.dump () << "\n"; return all; } @@ -306,9 +323,12 @@ std::vector subset ( { std::vector all; for (auto& interval : intervals) - if (intervalMatchesFilter (interval, filter)) + if (matchesFilter (interval, filter)) all.push_back (interval); + std::cout << "# subset (filter intervals):\n"; + for (auto& i : all) + std::cout << "# " << i.dump () << "\n"; return all; } @@ -322,6 +342,9 @@ std::vector subset ( if (range.overlap (r)) all.push_back (r); + std::cout << "# subset (ranges):\n"; + for (auto& r : all) + std::cout << "# " << r.dump () << "\n"; return all; } @@ -335,11 +358,14 @@ std::vector subset ( if (range.overlap (interval.range)) all.push_back (interval); + std::cout << "# subset (intervals):\n"; + for (auto& i : all) + std::cout << "# " << i.dump () << "\n"; return all; } //////////////////////////////////////////////////////////////////////////////// -std::vector realize ( +std::vector collapse ( const Interval& interval, const std::vector & exclusions) { @@ -357,15 +383,13 @@ std::vector realize ( pieces = split_pieces; } - // Return all the fragments as intervals. + // Return all the fragments as clipped intervals. for (auto& piece : pieces) - { - // Clone the interval, override the range. - Interval clipped {interval}; - clipped.range = piece; - all.push_back (clipped); - } + all.push_back (clip (interval, piece)); + std::cout << "# collapse:\n"; + for (auto& i : all) + std::cout << "# " << i.dump () << "\n"; return all; } @@ -386,6 +410,9 @@ std::vector addRanges ( if (limits.overlap (addition)) results.push_back (addition); + std::cout << "# addRange:\n"; + for (auto& result : results) + std::cout << "# " << result.dump () << "\n"; return results; } @@ -406,6 +433,9 @@ std::vector subtractRanges ( for (auto& r3 : r1.subtract (r2)) results.push_back (limits.intersect (r3)); + std::cout << "# addRange:\n"; + for (auto& result : results) + std::cout << "# " << result.dump () << "\n"; return results; } @@ -414,22 +444,92 @@ std::vector subtractRanges ( // return these in a Range. Range outerRange (const std::vector & intervals) { - Range overall; - + Range outer; for (auto& interval : intervals) { - if (interval.range.start < overall.start || overall.start.toEpoch () == 0) - overall.start = interval.range.start; + if (interval.range.start < outer.start || outer.start.toEpoch () == 0) + outer.start = interval.range.start; // Deliberately mixed start/end. - if (interval.range.start > overall.end) - overall.end = interval.range.start; + if (interval.range.start > outer.end) + outer.end = interval.range.start; - if (interval.range.end > overall.end) - overall.end = interval.range.end; + if (interval.range.end > outer.end) + outer.end = interval.range.end; + + if (! interval.range.ended ()) + outer.end = Datetime (); } - return overall; + std::cout << "# outerRange " << outer.dump () << "\n"; + return outer; +} + +//////////////////////////////////////////////////////////////////////////////// +// An interval matches a filter interval if the start/end overlaps, and all +// filter interval tags are found in the interval. +bool matchesFilter (const Interval& interval, const Interval& filter) +{ + if ((filter.range.start.toEpoch () == 0 && + filter.range.end.toEpoch () == 0) + + || + + ((interval.range.end.toEpoch () == 0 || + interval.range.end > filter.range.start) && + interval.range.start < filter.range.end)) + { + for (auto& tag : filter.tags ()) + if (! interval.hasTag (tag)) + return false; + + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Take an interval and clip it to the range. +Interval clip (const Interval& interval, const Range& range) +{ + Interval clipped {interval}; + clipped.range = clipped.range.intersect (range); + return clipped; +} + +//////////////////////////////////////////////////////////////////////////////// +std::vector getTrackedIntervals ( + Database& database, + const Rules& rules, + Interval& filter) +{ + auto inclusions = getAllInclusions (database); + + if (! filter.range.started ()) + filter.range = outerRange (inclusions); + + // Get the set of expanded exclusions that overlap the range defined by the + // timeline. If no range is defined, derive it from the set of all data. + auto exclusions = getAllExclusions (rules, filter.range); + + std::vector intervals; + for (auto& inclusion : subset (filter, inclusions)) + for (auto& interval : collapse (clip (inclusion, filter.range), exclusions)) + intervals.push_back (interval); + + return intervals; +} + +//////////////////////////////////////////////////////////////////////////////// +Interval getLatestInterval (Database& database) +{ + Interval i; + auto lastLine = database.lastLine (); + if (lastLine != "") + i.initialize (lastLine); + + return i; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/helper.cpp b/src/helper.cpp index 318e262e..38e0e039 100644 --- a/src/helper.cpp +++ b/src/helper.cpp @@ -31,6 +31,7 @@ #include #include #include +#include // TODO Remove. #include #include @@ -117,248 +118,37 @@ bool expandIntervalHint ( } //////////////////////////////////////////////////////////////////////////////// -// A filter is just another interval, containing start, end and tags. -// -// Supported interval forms: -// ["from"] ["to"|"-" ] -// ["from"] "for" -// ["before"|"after" ] -// -Interval getFilter (const CLI& cli) +// Excluded time is that which is not available for work. +/* +std::vector getExcludedRanges (Rules& rules, Range& range) { - Interval filter; - std::string start; - std::string end; - std::string duration; + // Create a range representing the whole timeline. + // If no range is defined, then assume the full range of all the inclusions. + Range overallRange {range}; + if (! overallRange.started () && + ! overallRange.ended ()) + overallRange = outerRange (getInclusions (database)); - std::vector args; - for (auto& arg : cli._args) - { - if (arg.hasTag ("BINARY") || - arg.hasTag ("CMD") || - arg.hasTag ("EXT")) - continue; - - if (arg.hasTag ("FILTER")) - { - auto canonical = arg.attribute ("canonical"); - auto raw = arg.attribute ("raw"); - - if (arg.hasTag ("HINT")) - { - if (expandIntervalHint (canonical, start, end)) - { - args.push_back (""); - args.push_back ("-"); - args.push_back (""); - } - - // Hints that are not expandable to a date range are ignored. - } - else if (arg._lextype == Lexer::Type::date) - { - if (start == "") - start = raw; - else if (end == "") - end = raw; - - args.push_back (""); - } - else if (arg._lextype == Lexer::Type::duration) - { - if (duration == "") - duration = raw; - - args.push_back (""); - } - else if (arg.hasTag ("KEYWORD")) - { - args.push_back (raw); - } - else - { - filter.tag (raw); - } - } - } - - Range range; - - // - if (args.size () == 1 && - args[0] == "") - { - range.start = Datetime (start); - range.end = Datetime ("now"); - } - - // from - else if (args.size () == 2 && - args[0] == "from" && - args[1] == "") - { - range.start = Datetime (start); - range.end = Datetime ("now"); - } - - // to/- - else if (args.size () == 3 && - args[0] == "" && - (args[1] == "to" || args[1] == "-") && - args[2] == "") - { - range.start = Datetime (start); - range.end = Datetime (end); - } - - // from/since to/- - else if (args.size () == 4 && - args[0] == "from" && - args[1] == "" && - (args[2] == "to" || args[2] == "-") && - args[3] == "") - { - range.start = Datetime (start); - range.end = Datetime (end); - } - - // for - else if (args.size () == 3 && - args[0] == "" && - args[1] == "for" && - args[2] == "") - { - range.start = Datetime (start); - range.end = Datetime (start) + Duration (duration).toTime_t (); - } - - // from/since for - else if (args.size () == 4 && - (args[0] == "from" || args[0] == "since") && - args[1] == "" && - args[2] == "for" && - args[3] == "") - { - range.start = Datetime (start); - range.end = Datetime (start) + Duration (duration).toTime_t (); - } - - // before - else if (args.size () == 3 && - args[0] == "" && - args[1] == "before" && - args[2] == "") - { - range.start = Datetime (start) - Duration (duration).toTime_t (); - range.end = Datetime (start); - } - - // after - else if (args.size () == 3 && - args[0] == "" && - args[1] == "after" && - args[2] == "") - { - range.start = Datetime (start); - range.end = Datetime (start) + Duration (duration).toTime_t (); - } - - // - else if (args.size () == 1 && - args[0] == "") - { - range.start = Datetime ("now") - Duration (duration).toTime_t (); - range.end = Datetime ("now"); - } - - // Unrecognized date range construct. - else if (args.size ()) - { - throw std::string ("Unrecognized date range: '") + join (" ", args) + "'."; - } - - filter.range = range; - return filter; + // Cobmine all the non-trackable time. + return getAllExclusions (rules, overallRange); } - +*/ //////////////////////////////////////////////////////////////////////////////// -// The five different overlap possibilities: -// -// timeline.start timeline.end -// | | -// A [--------) | | -// B [----|----) | -// C | [------------) | -// D | [----|----) -// E | | [--------) -// F [----|-------------------|----) -// | | -// -// We really only need to eliminate A and F. -// -Timeline createTimelineFromData ( - const Rules& rules, - Database& database, - const Interval& filter) +// Untracked time is that which is not excluded, and not filled. Gaps. +std::vector getUntrackedRanges (Rules& rules) { - Timeline t; - t.range = filter.range; + std::vector gaps; - // Add filtered intervals. - for (auto& line : database.allLines ()) - { - Interval i; - i.initialize (line); - if (intervalMatchesFilterInterval (i, filter)) - t.include (i); - } + // Get the set of expanded exclusions that overlap the range defined by the + // timeline. If no range is defined, derive it from the set of all data. +/* + auto exclusions = getExcludedRanges (rules); +*/ - // Add exclusions from configuration. - for (auto& name : rules.all ("exclusions.")) - t.exclude (Exclusion (lowerCase (name), rules.get (name))); + // TODO subtract all exclusions + // TODO subtract all inclusions - return t; -} - -//////////////////////////////////////////////////////////////////////////////// -Interval getLatestInterval (Database& database) -{ - Interval i; - auto lastLine = database.lastLine (); - if (lastLine != "") - { - i.initialize (lastLine); - if (! i.range.ended ()) - { - // TODO An open interval needs to be split against all exclusions. - } - } - - return i; -} - -//////////////////////////////////////////////////////////////////////////////// -// An interval matches a filter interval if the start/end overlaps, and all -// filter interval tags are found in the interval. -bool intervalMatchesFilterInterval (const Interval& interval, const Interval& filter) -{ - if ((filter.range.start.toEpoch () == 0 && - filter.range.end.toEpoch () == 0) - - || - - ((interval.range.end.toEpoch () == 0 || - interval.range.end > filter.range.start) && - interval.range.start < filter.range.end)) - { - for (auto& tag : filter.tags ()) - if (! interval.hasTag (tag)) - return false; - - return true; - } - - return false; + return gaps; } //////////////////////////////////////////////////////////////////////////////// @@ -394,147 +184,6 @@ std::string jsonFromIntervals (const std::vector & intervals) return out.str (); } -//////////////////////////////////////////////////////////////////////////////// -// Read rules and extract all holiday definitions. Create a Range for each -// one that spans from midnight to midnight. -std::vector rangesFromHolidays (const Rules& rules) -{ - std::vector results; - for (auto& holiday : rules.all ("holidays.")) - { - auto lastDot = holiday.rfind ('.'); - if (lastDot != std::string::npos) - { - Range r; - Datetime d (holiday.substr (lastDot + 1), "Y_M_D"); - r.start = d; - ++d; - r.end = d; - results.push_back (r); - } - } - - return results; -} - -//////////////////////////////////////////////////////////////////////////////// -// Subset both ranges and additions by limits, and combine. -std::vector addRanges ( - const Range& limits, - const std::vector & ranges, - const std::vector & additions) -{ - std::vector results; - - for (auto& range : ranges) - if (limits.overlap (range)) - results.push_back (range); - - for (auto& addition : additions) - if (limits.overlap (addition)) - results.push_back (addition); - - return results; -} - -//////////////////////////////////////////////////////////////////////////////// -// Subtract a set of Range from another set of Range, all within a defined -// range. -std::vector subtractRanges ( - const Range& limits, - const std::vector & ranges, - const std::vector & subtractions) -{ - if (! subtractions.size ()) - return ranges; - - std::vector results; - for (auto& r1 : ranges) - for (auto& r2 : subtractions) - for (auto& r3 : r1.subtract (r2)) - results.push_back (limits.intersect (r3)); - - return results; -} - -//////////////////////////////////////////////////////////////////////////////// -// From a set of intervals, find the earliest start and the latest end, and -// return these in a Range. -Range overallRangeFromIntervals (const std::vector & intervals) -{ - Range overall; - - for (auto& interval : intervals) - { - if (interval.range.start < overall.start || overall.start.toEpoch () == 0) - overall.start = interval.range.start; - - // Deliberately mixed start/end. - if (interval.range.start > overall.end) - overall.end = interval.range.start; - - if (interval.range.end > overall.end) - overall.end = interval.range.end; - } - - return overall; -} - -//////////////////////////////////////////////////////////////////////////////// -// [1] Read holiday definitions from the rules, extract their dates and create -// a set of Range from them. -// [2] For 'exc day ...' exclusions, separate into daysOn and daysOff sets, -// based on whether the exclusion is additive. -// [3] Treat daysOff as additional holidays. -// [4] Subtract daysOn from the set of holidays. -// [5] Take all the 'exc ...' exclusions and expand them into -// concrete ranges within the overall range, adding them to the results. -// -// The result is the complete set of untrackable time that lies within the -// input range. This will be a set of nights, weekends, holidays and lunchtimes. -std::vector combineHolidaysAndExclusions ( - const Range& range, - const Rules& rules, - const std::vector & exclusions) -{ - // Start with the set of all holidays, intersected with range. - std::vector results; - results = addRanges (range, results, rangesFromHolidays (rules)); - - // Find exclusions 'exc day on ' and remove from holidays. - // Find exlcusions 'exc day off ' and add to holidays. - std::vector daysOn; - std::vector daysOff; - for (auto& exclusion : exclusions) - { - if (exclusion.tokens ()[1] == "day") - { - if (exclusion.additive ()) - for (auto& r : exclusion.ranges (range)) - daysOn.push_back (r); - else - for (auto& r : exclusion.ranges (range)) - daysOff.push_back (r); - } - } - - // daysOff are combined with existing holidays. - results = addRanges (range, results, daysOff); - - // daysOn are subtracted from the existing holidays. - results = subtractRanges (range, results, daysOn); - - // Expand all exclusions that are not 'exc day ...' into excluded ranges that - // overlage with range. - std::vector exclusionRanges; - for (auto& exclusion : exclusions) - if (exclusion.tokens ()[1] != "day") - for (auto& r : exclusion.ranges (range)) - exclusionRanges.push_back (r); - - return addRanges (range, results, exclusionRanges); -} - //////////////////////////////////////////////////////////////////////////////// Palette createPalette (const Rules& rules) { @@ -587,34 +236,3 @@ int quantizeTo15Minutes (const int minutes) } //////////////////////////////////////////////////////////////////////////////// -std::vector splitInterval ( - const Interval& interval, - const std::vector & exclusions) -{ - std::vector all; - - // Start with a single range from the interval, from which to subtract. - std::vector intervalFragments {interval.range}; - for (auto& exclusion : exclusions) - { - std::vector brokenFragments; - for (auto& fragment : intervalFragments) - for (auto& broken : fragment.subtract (exclusion)) - brokenFragments.push_back (broken); - - intervalFragments = brokenFragments; - } - - // Return all the fragments as intervals. - for (auto& fragment : intervalFragments) - { - // Clone the interval, set the new range. - Interval clipped {interval}; - clipped.range = fragment; - all.push_back (clipped); - } - - return all; -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/timew.h b/src/timew.h index b89722b2..de7632a9 100644 --- a/src/timew.h +++ b/src/timew.h @@ -32,10 +32,28 @@ #include #include #include -#include +#include #include #include +// data.cpp +Interval getFilter (const CLI&); +std::vector getHolidays (const Rules&); +std::vector getExclusions (const Rules&); +std::vector getAllExclusions (const Rules&, const Range&); +std::vector getAllInclusions (Database&); +std::vector subset (const Interval&, const std::vector &); +std::vector subset (const Range&, const std::vector &); +std::vector subset (const Range&, const std::vector &); +std::vector collapse (const Interval&, const std::vector &); +std::vector addRanges (const Range&, const std::vector &, const std::vector &); +std::vector subtractRanges (const Range&, const std::vector &, const std::vector &); +Range outerRange (const std::vector &); +bool matchesFilter (const Interval&, const Interval&); +Interval clip (const Interval&, const Range&); +std::vector getTrackedIntervals (Database&, const Rules&, Interval&); +Interval getLatestInterval (Database&); + // init.cpp bool lightweightVersionCheck (int, const char**); void initializeEntities (CLI&); @@ -47,20 +65,12 @@ int dispatchCommand (const CLI&, Database&, Rules&, const Extensions&); Color tagColor (const Rules&, const std::string&); std::string intervalSummarize (const Rules&, const Interval&); bool expandIntervalHint (const std::string&, std::string&, std::string&); -Timeline createTimelineFromData (const Rules&, Database&, const Interval&); -Interval getFilter (const CLI&); -Interval getLatestInterval (Database&); -bool intervalMatchesFilterInterval (const Interval&, const Interval&); +std::vector getExcludedRanges (Rules&, Range&); +std::vector getUntrackedRanges (Rules&); std::string jsonFromIntervals (const std::vector &); -std::vector rangesFromHolidays (const Rules&); -std::vector addRanges (const Range&, const std::vector &, const std::vector &); -std::vector subtractRanges (const Range&, const std::vector &, const std::vector &); -Range overallRangeFromIntervals (const std::vector &); -std::vector combineHolidaysAndExclusions (const Range&, const Rules&, const std::vector &); Palette createPalette (const Rules&); std::map createTagColorMap (const Rules&, Palette&, const std::vector &); int quantizeTo15Minutes (const int); -std::vector splitInterval (const Interval&, const std::vector &); // utiƀ.cpp std::string osName ();