From 4db7990df8699b366f5374013699159279b87ee7 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sat, 12 Jun 2021 10:37:23 -0400 Subject: [PATCH] recur: Remove WIP recurrence overhaul implementation The feature has been moved to 3.0 milestone. Also, we are now using feature branches for development of new functionality, in order to make the development branch more stable. --- src/main.h | 2 +- src/recur.cpp | 24 ++++ src/recur2.cpp | 373 ------------------------------------------------- 3 files changed, 25 insertions(+), 374 deletions(-) delete mode 100644 src/recur2.cpp diff --git a/src/main.h b/src/main.h index aa7ade643..38a217137 100644 --- a/src/main.h +++ b/src/main.h @@ -39,13 +39,13 @@ // recur.cpp void handleRecurrence (); +void handleUntil (); Datetime getNextRecurrence (Datetime&, std::string&); bool generateDueDates (Task&, std::vector &); void updateRecurrenceMask (Task&); // recur2.cpp void handleRecurrence2 (); -void handleUntil (); // nag.cpp bool nag (Task&); diff --git a/src/recur.cpp b/src/recur.cpp index 61bb169b9..627e545e9 100644 --- a/src/recur.cpp +++ b/src/recur.cpp @@ -412,3 +412,27 @@ void updateRecurrenceMask (Task& task) } //////////////////////////////////////////////////////////////////////////////// +// Delete expired tasks. +void handleUntil () +{ + Datetime now; + auto tasks = Context::getContext ().tdb2.pending.get_tasks (); + for (auto& t : tasks) + { + // TODO What about expiring template tasks? + if (t.getStatus () == Task::pending && + t.has ("until")) + { + auto until = Datetime (t.get_date ("until")); + if (until < now) + { + Context::getContext ().debug (format ("handleUntil: recurrence expired until {1} < now {2}", until.toISOLocalExtended (), now.toISOLocalExtended ())); + t.setStatus (Task::deleted); + Context::getContext ().tdb2.modify(t); + Context::getContext ().footnote (onExpiration (t)); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/recur2.cpp b/src/recur2.cpp deleted file mode 100644 index 55c06ac54..000000000 --- a/src/recur2.cpp +++ /dev/null @@ -1,373 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2006 - 2021, Paul Beckingham, Federico Hernandez. -// -// 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 -#include -#include -#include - -//////////////////////////////////////////////////////////////////////////////// -// Checklist: -// - last: Most recently generated instance integer. The first instance -// generated is '1'. -// - Sync must merge duplicate N. -// - Remove recurrence.limit. Now always 1. It is not something that can be done -// with rtype:chained tasks. -// - Handle until. - -//////////////////////////////////////////////////////////////////////////////// -// Given an old-style task, upgrades it perfectly. -// Note: Works for both parent and child. -static Task upgradeTask (const Task&) -{ - Task upgraded; - - // TODO Convert 'mask' to 'last' <-- length (mask) - // TODO Convert 'parent' to 'template'. - // TODO Convert 'imask' to 'index'. - // TODO Add 'rtype:periodic'. - return upgraded; -} - -//////////////////////////////////////////////////////////////////////////////// -// Calculates the due date for a new instance N. -static Datetime generateNextDueDate ( - const Datetime& first, - const std::string& period, - const int n) -{ - auto y = first.year (); - auto m = first.month (); - auto d = first.day (); - auto hh = first.hour (); - auto mm = first.minute (); - auto ss = first.second (); - - Duration dur (period); - auto normalized = dur.formatISO (); - Context::getContext ().debug (" period " + period + " --> " + normalized); - - if (! dur._year && - dur._month && - ! dur._weeks && - ! dur._day && - ! dur._hours && - ! dur._minutes && - ! dur._seconds) - { - m += dur._month * n; - while (m > 12) - { - y += 1; - m -= 12; - } - - while (! Datetime::valid (y, m, d)) - --d; - - return Datetime (y, m, d, hh, mm, ss); - } - -/* - // Some periods are difficult, because they can be vague. - if (period == "monthly" || - period == "P1M") - { - if (++m > 12) - { - m -= 12; - ++y; - } - - while (! Datetime::valid (y, m, d)) - --d; - - return Datetime (y, m, d, ho, mi, se); - } - - else if (period == "weekdays") - { - auto dow = current.dayOfWeek (); - int days; - - if (dow == 5) days = 3; - else if (dow == 6) days = 2; - else days = 1; - - return current + (days * 86400); - } - - else if (unicodeLatinDigit (period[0]) && - period[period.length () - 1] == 'm') - { - int increment = strtol (period.substr (0, period.length () - 1).c_str (), NULL, 10); - - m += increment; - while (m > 12) - { - m -= 12; - ++y; - } - - while (! Datetime::valid (y, m, d)) - --d; - - return Datetime (y, m, d, ho, mi, se); - } - - else if (period[0] == 'P' && - Lexer::isAllDigits (period.substr (1, period.length () - 2)) && - period[period.length () - 1] == 'M') - { - int increment = strtol (period.substr (0, period.length () - 1).c_str (), NULL, 10); - - m += increment; - while (m > 12) - { - m -= 12; - ++y; - } - - while (! Datetime::valid (y, m, d)) - --d; - - return Datetime (y, m, d); - } - - else if (period == "quarterly" || - period == "P3M") - { - m += 3; - if (m > 12) - { - m -= 12; - ++y; - } - - while (! Datetime::valid (y, m, d)) - --d; - - return Datetime (y, m, d, ho, mi, se); - } - - else if (unicodeLatinDigit (period[0]) && period[period.length () - 1] == 'q') - { - int increment = strtol (period.substr (0, period.length () - 1).c_str (), NULL, 10); - - m += 3 * increment; - while (m > 12) - { - m -= 12; - ++y; - } - - while (! Datetime::valid (y, m, d)) - --d; - - return Datetime (y, m, d, ho, mi, se); - } - - else if (period == "semiannual" || - period == "P6M") - { - m += 6; - if (m > 12) - { - m -= 12; - ++y; - } - - while (! Datetime::valid (y, m, d)) - --d; - - return Datetime (y, m, d, ho, mi, se); - } - - else if (period == "bimonthly" || - period == "P2M") - { - m += 2; - if (m > 12) - { - m -= 12; - ++y; - } - - while (! Datetime::valid (y, m, d)) - --d; - - return Datetime (y, m, d, ho, mi, se); - } - - else if (period == "biannual" || - period == "biyearly" || - period == "P2Y") - { - y += 2; - - return Datetime (y, m, d, ho, mi, se); - } - - else if (period == "annual" || - period == "yearly" || - period == "P1Y") - { - y += 1; - - // If the due data just happens to be 2/29 in a leap year, then simply - // incrementing y is going to create an invalid date. - if (m == 2 && d == 29) - d = 28; - - return Datetime (y, m, d, ho, mi, se); - } - - // Add the period to current, and we're done. - std::string::size_type idx = 0; - Duration p; - if (! p.parse (period, idx)) - throw std::string (format ("The recurrence value '{1}' is not valid.", period)); - - return current + p.toTime_t (); -*/ - - Datetime due; - return due; -} - -//////////////////////////////////////////////////////////////////////////////// -// Calculates the due date for a new instance N. -static std::vector generateAllDueDates (const Task& templateTask) -{ - std::vector dueDates; - - // Determine due date, recur period and until date. - Datetime due (templateTask.get_date ("due")); - Context::getContext ().debug (" due " + due.toISOLocalExtended ()); - - auto recur = templateTask.get ("recur"); - Context::getContext ().debug (" recur " + recur); - - auto lastN = std::max (1, templateTask.get_int ("last")); - Context::getContext ().debug (format (" last {1}", lastN)); - - bool end_in_sight = false; - Datetime until; - if (templateTask.get ("until") != "") - { - until = Datetime (templateTask.get ("until")); - end_in_sight = true; - Context::getContext ().debug (" until " + until.toISOLocalExtended ()); - } - - auto recurrence_limit = Context::getContext ().config.getInteger ("recurrence.limit"); - Context::getContext ().debug (format (" recurrence.limit {1}", recurrence_limit)); - int recurrence_counter = 0; - Datetime now; - - while (true) - { - Datetime nextDue = generateNextDueDate (due, recur, lastN); - - // TODO Safety. - if (dueDates.size () && dueDates.back () == nextDue) - break; - - // If nextDue > until, it means there are no more tasks to generate, so - // this templateTask is finished. - if (end_in_sight && nextDue > until) - break; - - if (nextDue > now) - ++recurrence_counter; - - if (recurrence_counter >= recurrence_limit) - break; - - dueDates.push_back (nextDue); - } - - return dueDates; -} - -//////////////////////////////////////////////////////////////////////////////// -static void synthesizeTasks (const Task& templateTask) -{ - Context::getContext ().debug ("synthesizeTasks start"); - Context::getContext ().debug (" template " + templateTask.get ("uuid")); - - // TODO 'due' = starting point - // TODO 'recur' = frequency - // TODO 'last' = index of most recently synthesized instance - - auto all = generateAllDueDates (templateTask); - for (auto& date : all) - Context::getContext ().debug (" date " + date.toISOLocalExtended ()); - - // TODO Create task instances for each period between N and now. - - Context::getContext ().debug ("synthesizeTasks end"); -} - -//////////////////////////////////////////////////////////////////////////////// -// Generates all necessary recurring task instances. -void handleRecurrence2 () -{ - // Note: Disabling recurrence is currently a workaround for TD-44, TW-1520. - if (Context::getContext ().config.getBoolean ("recurrence")) - for (auto& t : Context::getContext ().tdb2.pending.get_tasks ()) - if (t.getStatus () == Task::recurring) - synthesizeTasks (t); -} - -//////////////////////////////////////////////////////////////////////////////// -// Delete expired tasks. -void handleUntil () -{ - Datetime now; - auto tasks = Context::getContext ().tdb2.pending.get_tasks (); - for (auto& t : tasks) - { - // TODO What about expiring template tasks? - if (t.getStatus () == Task::pending && - t.has ("until")) - { - auto until = Datetime (t.get_date ("until")); - if (until < now) - { - Context::getContext ().debug (format ("handleUntil: recurrence expired until {1} < now {2}", until.toISOLocalExtended (), now.toISOLocalExtended ())); - t.setStatus (Task::deleted); - Context::getContext ().tdb2.modify(t); - Context::getContext ().footnote (onExpiration (t)); - } - } - } -} - -////////////////////////////////////////////////////////////////////////////////