mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
- Recurring tasks!
This commit is contained in:
parent
46ded4c026
commit
6f7b9b7d42
13 changed files with 395 additions and 188 deletions
2
AUTHORS
2
AUTHORS
|
@ -13,4 +13,6 @@ With thanks to:
|
||||||
Nishiishii
|
Nishiishii
|
||||||
galvanizd
|
galvanizd
|
||||||
H. İbrahim Güngör
|
H. İbrahim Güngör
|
||||||
|
Stas Antons
|
||||||
|
Andy Lester
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ represents a feature release, and the Z represents a patch.
|
||||||
|
|
||||||
|
|
||||||
1.4.0 ()
|
1.4.0 ()
|
||||||
|
+ New recurring tasks feature
|
||||||
+ "task undelete" can now undelete erroneously deleted tasks, provided no
|
+ "task undelete" can now undelete erroneously deleted tasks, provided no
|
||||||
reports have been run (and therefore TDB::gc run)
|
reports have been run (and therefore TDB::gc run)
|
||||||
+ Added averages to the "task history" report
|
+ Added averages to the "task history" report
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>Added new recurring tasks feature
|
||||||
<li>Added "task undelete" feature to restore a (very) recently deleted
|
<li>Added "task undelete" feature to restore a (very) recently deleted
|
||||||
task
|
task
|
||||||
<li>Added averages to the "task history" report
|
<li>Added averages to the "task history" report
|
||||||
|
|
|
@ -45,8 +45,8 @@ binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
|
||||||
PROGRAMS = $(bin_PROGRAMS)
|
PROGRAMS = $(bin_PROGRAMS)
|
||||||
am_task_OBJECTS = Config.$(OBJEXT) Date.$(OBJEXT) T.$(OBJEXT) \
|
am_task_OBJECTS = Config.$(OBJEXT) Date.$(OBJEXT) T.$(OBJEXT) \
|
||||||
TDB.$(OBJEXT) Table.$(OBJEXT) Grid.$(OBJEXT) color.$(OBJEXT) \
|
TDB.$(OBJEXT) Table.$(OBJEXT) Grid.$(OBJEXT) color.$(OBJEXT) \
|
||||||
parse.$(OBJEXT) task.$(OBJEXT) util.$(OBJEXT) text.$(OBJEXT) \
|
parse.$(OBJEXT) task.$(OBJEXT) command.$(OBJEXT) \
|
||||||
rules.$(OBJEXT)
|
report.$(OBJEXT) util.$(OBJEXT) text.$(OBJEXT) rules.$(OBJEXT)
|
||||||
task_OBJECTS = $(am_task_OBJECTS)
|
task_OBJECTS = $(am_task_OBJECTS)
|
||||||
task_LDADD = $(LDADD)
|
task_LDADD = $(LDADD)
|
||||||
DEFAULT_INCLUDES = -I. -I$(top_builddir)@am__isrc@
|
DEFAULT_INCLUDES = -I. -I$(top_builddir)@am__isrc@
|
||||||
|
@ -154,7 +154,7 @@ sysconfdir = @sysconfdir@
|
||||||
target_alias = @target_alias@
|
target_alias = @target_alias@
|
||||||
top_builddir = @top_builddir@
|
top_builddir = @top_builddir@
|
||||||
top_srcdir = @top_srcdir@
|
top_srcdir = @top_srcdir@
|
||||||
task_SOURCES = Config.cpp Date.cpp T.cpp TDB.cpp Table.cpp Grid.cpp color.cpp parse.cpp task.cpp util.cpp text.cpp rules.cpp Config.h Date.h T.h TDB.h Table.h Grid.h color.h task.h
|
task_SOURCES = Config.cpp Date.cpp T.cpp TDB.cpp Table.cpp Grid.cpp color.cpp parse.cpp task.cpp command.cpp report.cpp util.cpp text.cpp rules.cpp Config.h Date.h T.h TDB.h Table.h Grid.h color.h task.h
|
||||||
AM_CPPFLAGS = -Wall -pedantic -ggdb3 -fno-rtti
|
AM_CPPFLAGS = -Wall -pedantic -ggdb3 -fno-rtti
|
||||||
all: all-am
|
all: all-am
|
||||||
|
|
||||||
|
@ -229,7 +229,9 @@ distclean-compile:
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TDB.Po@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TDB.Po@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Table.Po@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Table.Po@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Po@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Po@am__quote@
|
||||||
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/command.Po@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@
|
||||||
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/report.Po@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rules.Po@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rules.Po@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/task.Po@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/task.Po@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/text.Po@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/text.Po@am__quote@
|
||||||
|
|
|
@ -450,7 +450,7 @@ void T::parse (const std::string& line)
|
||||||
{
|
{
|
||||||
std::vector <std::string> pair;
|
std::vector <std::string> pair;
|
||||||
split (pair, pairs[i], ':');
|
split (pair, pairs[i], ':');
|
||||||
if (pair[1] != "")
|
if (pair.size () == 2)
|
||||||
mAttributes[pair[0]] = pair[1];
|
mAttributes[pair[0]] = pair[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
src/T.h
2
src/T.h
|
@ -32,7 +32,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
// Length of longest line.
|
// Length of longest line.
|
||||||
#define T_LINE_MAX 8192
|
#define T_LINE_MAX 32768
|
||||||
|
|
||||||
class T
|
class T
|
||||||
{
|
{
|
||||||
|
|
31
src/TDB.cpp
31
src/TDB.cpp
|
@ -38,6 +38,7 @@ TDB::TDB ()
|
||||||
: mPendingFile ("")
|
: mPendingFile ("")
|
||||||
, mCompletedFile ("")
|
, mCompletedFile ("")
|
||||||
, mLogFile ("")
|
, mLogFile ("")
|
||||||
|
, mId (1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ void TDB::dataDirectory (const std::string& directory)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Combine allPendingT with allCompletedT.
|
// Combine allPendingT with allCompletedT.
|
||||||
// Note: this method is O(N1) + O(N2), where N2 is not bounded.
|
// Note: this method is O(N1) + O(N2), where N2 is not bounded.
|
||||||
bool TDB::allT (std::vector <T>& all) const
|
bool TDB::allT (std::vector <T>& all)
|
||||||
{
|
{
|
||||||
all.clear ();
|
all.clear ();
|
||||||
|
|
||||||
|
@ -95,20 +96,20 @@ bool TDB::allT (std::vector <T>& all) const
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Only accesses to the pending file result in Tasks that have assigned ids.
|
// Only accesses to the pending file result in Tasks that have assigned ids.
|
||||||
bool TDB::pendingT (std::vector <T>& all) const
|
bool TDB::pendingT (std::vector <T>& all)
|
||||||
{
|
{
|
||||||
all.clear ();
|
all.clear ();
|
||||||
|
|
||||||
std::vector <std::string> lines;
|
std::vector <std::string> lines;
|
||||||
if (readLockedFile (mPendingFile, lines))
|
if (readLockedFile (mPendingFile, lines))
|
||||||
{
|
{
|
||||||
int id = 1;
|
mId = 1;
|
||||||
|
|
||||||
std::vector <std::string>::iterator it;
|
std::vector <std::string>::iterator it;
|
||||||
for (it = lines.begin (); it != lines.end (); ++it)
|
for (it = lines.begin (); it != lines.end (); ++it)
|
||||||
{
|
{
|
||||||
T t (*it);
|
T t (*it);
|
||||||
t.setId (id++);
|
t.setId (mId++);
|
||||||
if (t.getStatus () == T::pending)
|
if (t.getStatus () == T::pending)
|
||||||
all.push_back (t);
|
all.push_back (t);
|
||||||
}
|
}
|
||||||
|
@ -121,20 +122,20 @@ bool TDB::pendingT (std::vector <T>& all) const
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Only accesses to the pending file result in Tasks that have assigned ids.
|
// Only accesses to the pending file result in Tasks that have assigned ids.
|
||||||
bool TDB::allPendingT (std::vector <T>& all) const
|
bool TDB::allPendingT (std::vector <T>& all)
|
||||||
{
|
{
|
||||||
all.clear ();
|
all.clear ();
|
||||||
|
|
||||||
std::vector <std::string> lines;
|
std::vector <std::string> lines;
|
||||||
if (readLockedFile (mPendingFile, lines))
|
if (readLockedFile (mPendingFile, lines))
|
||||||
{
|
{
|
||||||
int id = 1;
|
mId = 1;
|
||||||
|
|
||||||
std::vector <std::string>::iterator it;
|
std::vector <std::string>::iterator it;
|
||||||
for (it = lines.begin (); it != lines.end (); ++it)
|
for (it = lines.begin (); it != lines.end (); ++it)
|
||||||
{
|
{
|
||||||
T t (*it);
|
T t (*it);
|
||||||
t.setId (id++);
|
t.setId (mId++);
|
||||||
all.push_back (t);
|
all.push_back (t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +189,7 @@ bool TDB::allCompletedT (std::vector <T>& all) const
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool TDB::deleteT (const T& t) const
|
bool TDB::deleteT (const T& t)
|
||||||
{
|
{
|
||||||
T task (t);
|
T task (t);
|
||||||
|
|
||||||
|
@ -212,7 +213,7 @@ bool TDB::deleteT (const T& t) const
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool TDB::completeT (const T& t) const
|
bool TDB::completeT (const T& t)
|
||||||
{
|
{
|
||||||
T task (t);
|
T task (t);
|
||||||
|
|
||||||
|
@ -261,7 +262,7 @@ bool TDB::addT (const T& t) const
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool TDB::modifyT (const T& t) const
|
bool TDB::modifyT (const T& t)
|
||||||
{
|
{
|
||||||
T modified (t);
|
T modified (t);
|
||||||
|
|
||||||
|
@ -348,7 +349,7 @@ bool TDB::lock (FILE* file) const
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool TDB::overwritePending (std::vector <T>& all) const
|
bool TDB::overwritePending (std::vector <T>& all)
|
||||||
{
|
{
|
||||||
// Write a single task to the pending file
|
// Write a single task to the pending file
|
||||||
FILE* out;
|
FILE* out;
|
||||||
|
@ -453,7 +454,7 @@ bool TDB::readLockedFile (
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
int TDB::gc () const
|
int TDB::gc ()
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
|
@ -486,4 +487,10 @@ int TDB::gc () const
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
int TDB::nextId ()
|
||||||
|
{
|
||||||
|
return mId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
18
src/TDB.h
18
src/TDB.h
|
@ -38,22 +38,23 @@ public:
|
||||||
~TDB ();
|
~TDB ();
|
||||||
|
|
||||||
void dataDirectory (const std::string&);
|
void dataDirectory (const std::string&);
|
||||||
bool allT (std::vector <T>&) const;
|
bool allT (std::vector <T>&);
|
||||||
bool pendingT (std::vector <T>&) const;
|
bool pendingT (std::vector <T>&);
|
||||||
bool allPendingT (std::vector <T>&) const;
|
bool allPendingT (std::vector <T>&);
|
||||||
bool completedT (std::vector <T>&) const;
|
bool completedT (std::vector <T>&) const;
|
||||||
bool allCompletedT (std::vector <T>&) const;
|
bool allCompletedT (std::vector <T>&) const;
|
||||||
bool deleteT (const T&) const;
|
bool deleteT (const T&);
|
||||||
bool completeT (const T&) const;
|
bool completeT (const T&);
|
||||||
bool addT (const T&) const;
|
bool addT (const T&) const;
|
||||||
bool modifyT (const T&) const;
|
bool modifyT (const T&);
|
||||||
bool logRead (std::vector <std::string>&) const;
|
bool logRead (std::vector <std::string>&) const;
|
||||||
bool logCommand (int, char**) const;
|
bool logCommand (int, char**) const;
|
||||||
int gc () const;
|
int gc ();
|
||||||
|
int nextId ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool lock (FILE*) const;
|
bool lock (FILE*) const;
|
||||||
bool overwritePending (std::vector <T>&) const;
|
bool overwritePending (std::vector <T>&);
|
||||||
bool writePending (const T&) const;
|
bool writePending (const T&) const;
|
||||||
bool writeCompleted (const T&) const;
|
bool writeCompleted (const T&) const;
|
||||||
bool readLockedFile (const std::string&, std::vector <std::string>&) const;
|
bool readLockedFile (const std::string&, std::vector <std::string>&) const;
|
||||||
|
@ -62,6 +63,7 @@ private:
|
||||||
std::string mPendingFile;
|
std::string mPendingFile;
|
||||||
std::string mCompletedFile;
|
std::string mCompletedFile;
|
||||||
std::string mLogFile;
|
std::string mLogFile;
|
||||||
|
int mId;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -55,10 +55,8 @@ void handleAdd (const TDB& tdb, T& task, Config& conf)
|
||||||
std::map <std::string, std::string> atts;
|
std::map <std::string, std::string> atts;
|
||||||
task.getAttributes (atts);
|
task.getAttributes (atts);
|
||||||
foreach (i, atts)
|
foreach (i, atts)
|
||||||
{
|
|
||||||
if (i->second == "")
|
if (i->second == "")
|
||||||
task.removeAttribute (i->first);
|
task.removeAttribute (i->first);
|
||||||
}
|
|
||||||
|
|
||||||
// Recurring tasks get a special status.
|
// Recurring tasks get a special status.
|
||||||
if (task.getAttribute ("due") != "" &&
|
if (task.getAttribute ("due") != "" &&
|
||||||
|
@ -76,7 +74,7 @@ void handleAdd (const TDB& tdb, T& task, Config& conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void handleProjects (const TDB& tdb, T& task, Config& conf)
|
void handleProjects (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Get all the tasks, including deleted ones.
|
// Get all the tasks, including deleted ones.
|
||||||
std::vector <T> tasks;
|
std::vector <T> tasks;
|
||||||
|
@ -127,7 +125,7 @@ void handleProjects (const TDB& tdb, T& task, Config& conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void handleTags (const TDB& tdb, T& task, Config& conf)
|
void handleTags (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Get all the tasks.
|
// Get all the tasks.
|
||||||
std::vector <T> tasks;
|
std::vector <T> tasks;
|
||||||
|
@ -165,7 +163,7 @@ void handleTags (const TDB& tdb, T& task, Config& conf)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// If a task is deleted, but is still in the pending file, then it may be
|
// If a task is deleted, but is still in the pending file, then it may be
|
||||||
// undeleted simply by changing it's status.
|
// undeleted simply by changing it's status.
|
||||||
void handleUndelete (const TDB& tdb, T& task, Config& conf)
|
void handleUndelete (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
std::vector <T> all;
|
std::vector <T> all;
|
||||||
tdb.allPendingT (all);
|
tdb.allPendingT (all);
|
||||||
|
@ -289,44 +287,54 @@ void handleVersion (Config& conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void handleDelete (const TDB& tdb, T& task, Config& conf)
|
void handleDelete (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
if (conf.get ("confirmation") != "yes" || confirm ("Permanently delete task?"))
|
if (conf.get ("confirmation") != "yes" || confirm ("Permanently delete task?"))
|
||||||
|
{
|
||||||
|
std::vector <T> all;
|
||||||
|
tdb.allPendingT (all);
|
||||||
|
foreach (t, all)
|
||||||
|
{
|
||||||
|
if (t->getId () == task.getId ())
|
||||||
{
|
{
|
||||||
// Check for the more complex case of a recurring task. If this is a
|
// Check for the more complex case of a recurring task. If this is a
|
||||||
// recurring task, get confirmation to delete them all.
|
// recurring task, get confirmation to delete them all.
|
||||||
std::string parent = task.getAttribute ("parent");
|
std::string parent = t->getAttribute ("parent");
|
||||||
if (parent != "")
|
if (parent != "")
|
||||||
{
|
{
|
||||||
if (confirm ("This is a recurring task. Do you want to delete all pending recurrences of this same task?"))
|
if (confirm ("This is a recurring task. Do you want to delete all pending recurrences of this same task?"))
|
||||||
{
|
{
|
||||||
// Scan all pending tasks for siblings of this task, and the parent
|
// Scan all pending tasks for siblings of this task, and the parent
|
||||||
// itself, and delete them.
|
// itself, and delete them.
|
||||||
std::vector <T> all;
|
foreach (sibling, all)
|
||||||
tdb.allPendingT (all);
|
if (sibling->getAttribute ("parent") == parent ||
|
||||||
std::vector <T>::iterator it;
|
sibling->getUUID () == parent)
|
||||||
for (it = all.begin (); it != all.end (); ++it)
|
tdb.deleteT (*sibling);
|
||||||
if (it->getAttribute ("parent") == parent ||
|
|
||||||
it->getUUID () == parent)
|
|
||||||
tdb.deleteT (*it);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO Update mask in parent.
|
// Update mask in parent.
|
||||||
|
t->setStatus (T::deleted);
|
||||||
|
updateRecurrenceMask (tdb, all, *t);
|
||||||
|
tdb.deleteT (*t);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
tdb.deleteT (*t);
|
||||||
|
|
||||||
// No confirmation, just delete the one.
|
break; // No point continuing the loop.
|
||||||
tdb.deleteT (task);
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
std::cout << "Task not deleted." << std::endl;
|
std::cout << "Task not deleted." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void handleStart (const TDB& tdb, T& task, Config& conf)
|
void handleStart (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
std::vector <T> all;
|
std::vector <T> all;
|
||||||
tdb.pendingT (all);
|
tdb.pendingT (all);
|
||||||
|
@ -359,18 +367,29 @@ void handleStart (const TDB& tdb, T& task, Config& conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void handleDone (const TDB& tdb, T& task, Config& conf)
|
void handleDone (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
if (!tdb.completeT (task))
|
if (!tdb.completeT (task))
|
||||||
throw std::string ("Could not mark task as completed.");
|
throw std::string ("Could not mark task as completed.");
|
||||||
|
|
||||||
// TODO Now updates mask in parent.
|
// Now update mask in parent.
|
||||||
|
std::vector <T> all;
|
||||||
|
tdb.allPendingT (all);
|
||||||
|
foreach (t, all)
|
||||||
|
{
|
||||||
|
if (t->getId () == task.getId ())
|
||||||
|
{
|
||||||
|
t->setStatus (T::completed);
|
||||||
|
updateRecurrenceMask (tdb, all, *t);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nag (tdb, task, conf);
|
nag (tdb, task, conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void handleExport (const TDB& tdb, T& task, Config& conf)
|
void handleExport (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Use the description as a file name, then clobber the description so the
|
// Use the description as a file name, then clobber the description so the
|
||||||
// file name isn't used for filtering.
|
// file name isn't used for filtering.
|
||||||
|
@ -413,7 +432,7 @@ void handleExport (const TDB& tdb, T& task, Config& conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void handleModify (const TDB& tdb, T& task, Config& conf)
|
void handleModify (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
std::vector <T> all;
|
std::vector <T> all;
|
||||||
tdb.pendingT (all);
|
tdb.pendingT (all);
|
||||||
|
|
|
@ -108,7 +108,7 @@ void filter (std::vector<T>& all, T& task)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Successively apply filters based on the task object built from the command
|
// Successively apply filters based on the task object built from the command
|
||||||
// line. Tasks that match all the specified criteria are listed.
|
// line. Tasks that match all the specified criteria are listed.
|
||||||
void handleList (const TDB& tdb, T& task, Config& conf)
|
void handleList (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Determine window size, and set table accordingly.
|
// Determine window size, and set table accordingly.
|
||||||
int width = conf.get ("defaultwidth", 80);
|
int width = conf.get ("defaultwidth", 80);
|
||||||
|
@ -253,7 +253,7 @@ void handleList (const TDB& tdb, T& task, Config& conf)
|
||||||
// Successively apply filters based on the task object built from the command
|
// Successively apply filters based on the task object built from the command
|
||||||
// line. Tasks that match all the specified criteria are listed. Show a narrow
|
// line. Tasks that match all the specified criteria are listed. Show a narrow
|
||||||
// list that works better on mobile devices.
|
// list that works better on mobile devices.
|
||||||
void handleSmallList (const TDB& tdb, T& task, Config& conf)
|
void handleSmallList (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Determine window size, and set table accordingly.
|
// Determine window size, and set table accordingly.
|
||||||
int width = conf.get ("defaultwidth", 80);
|
int width = conf.get ("defaultwidth", 80);
|
||||||
|
@ -379,7 +379,7 @@ void handleSmallList (const TDB& tdb, T& task, Config& conf)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Successively apply filters based on the task object built from the command
|
// Successively apply filters based on the task object built from the command
|
||||||
// line. Tasks that match all the specified criteria are listed.
|
// line. Tasks that match all the specified criteria are listed.
|
||||||
void handleCompleted (const TDB& tdb, T& task, Config& conf)
|
void handleCompleted (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Determine window size, and set table accordingly.
|
// Determine window size, and set table accordingly.
|
||||||
int width = conf.get ("defaultwidth", 80);
|
int width = conf.get ("defaultwidth", 80);
|
||||||
|
@ -464,7 +464,7 @@ void handleCompleted (const TDB& tdb, T& task, Config& conf)
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Display all information for the given task.
|
// Display all information for the given task.
|
||||||
void handleInfo (const TDB& tdb, T& task, Config& conf)
|
void handleInfo (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Determine window size, and set table accordingly.
|
// Determine window size, and set table accordingly.
|
||||||
int width = conf.get ("defaultwidth", 80);
|
int width = conf.get ("defaultwidth", 80);
|
||||||
|
@ -548,6 +548,21 @@ void handleInfo (const TDB& tdb, T& task, Config& conf)
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Recur until");
|
table.addCell (row, 0, "Recur until");
|
||||||
table.addCell (row, 1, refTask.getAttribute ("until"));
|
table.addCell (row, 1, refTask.getAttribute ("until"));
|
||||||
|
|
||||||
|
row = table.addRow ();
|
||||||
|
table.addCell (row, 0, "Mask");
|
||||||
|
table.addCell (row, 1, refTask.getAttribute ("mask"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refTask.getAttribute ("parent") != "")
|
||||||
|
{
|
||||||
|
row = table.addRow ();
|
||||||
|
table.addCell (row, 0, "Parent task");
|
||||||
|
table.addCell (row, 1, refTask.getAttribute ("parent"));
|
||||||
|
|
||||||
|
row = table.addRow ();
|
||||||
|
table.addCell (row, 0, "Mask Index");
|
||||||
|
table.addCell (row, 1, refTask.getAttribute ("imask"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// due (colored)
|
// due (colored)
|
||||||
|
@ -645,7 +660,7 @@ void handleInfo (const TDB& tdb, T& task, Config& conf)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Successively apply filters based on the task object built from the command
|
// Successively apply filters based on the task object built from the command
|
||||||
// line. Tasks that match all the specified criteria are listed.
|
// line. Tasks that match all the specified criteria are listed.
|
||||||
void handleLongList (const TDB& tdb, T& task, Config& conf)
|
void handleLongList (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Determine window size, and set table accordingly.
|
// Determine window size, and set table accordingly.
|
||||||
int width = conf.get ("defaultwidth", 80);
|
int width = conf.get ("defaultwidth", 80);
|
||||||
|
@ -814,7 +829,7 @@ void handleLongList (const TDB& tdb, T& task, Config& conf)
|
||||||
// Project Tasks Avg Age Status
|
// Project Tasks Avg Age Status
|
||||||
// A 12 13d XXXXXXXX------
|
// A 12 13d XXXXXXXX------
|
||||||
// B 109 3d 12h XX------------
|
// B 109 3d 12h XX------------
|
||||||
void handleReportSummary (const TDB& tdb, T& task, Config& conf)
|
void handleReportSummary (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Generate unique list of project names.
|
// Generate unique list of project names.
|
||||||
tdb.gc ();
|
tdb.gc ();
|
||||||
|
@ -984,7 +999,7 @@ void handleReportSummary (const TDB& tdb, T& task, Config& conf)
|
||||||
//
|
//
|
||||||
// Make the "three" tasks a configurable number
|
// Make the "three" tasks a configurable number
|
||||||
//
|
//
|
||||||
void handleReportNext (const TDB& tdb, T& task, Config& conf)
|
void handleReportNext (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Load all pending.
|
// Load all pending.
|
||||||
tdb.gc ();
|
tdb.gc ();
|
||||||
|
@ -1156,7 +1171,7 @@ time_t monthlyEpoch (const std::string& date)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleReportHistory (const TDB& tdb, T& task, Config& conf)
|
void handleReportHistory (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
std::map <time_t, int> groups;
|
std::map <time_t, int> groups;
|
||||||
std::map <time_t, int> addedGroup;
|
std::map <time_t, int> addedGroup;
|
||||||
|
@ -1338,7 +1353,7 @@ void handleReportHistory (const TDB& tdb, T& task, Config& conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void handleReportGHistory (const TDB& tdb, T& task, Config& conf)
|
void handleReportGHistory (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Determine window size, and set table accordingly.
|
// Determine window size, and set table accordingly.
|
||||||
int width = conf.get ("defaultwidth", 80);
|
int width = conf.get ("defaultwidth", 80);
|
||||||
|
@ -1755,7 +1770,7 @@ std::string renderMonths (
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void handleReportCalendar (const TDB& tdb, T& task, Config& conf)
|
void handleReportCalendar (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Load all the pending tasks.
|
// Load all the pending tasks.
|
||||||
tdb.gc ();
|
tdb.gc ();
|
||||||
|
@ -1841,7 +1856,7 @@ void handleReportCalendar (const TDB& tdb, T& task, Config& conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void handleReportActive (const TDB& tdb, T& task, Config& conf)
|
void handleReportActive (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Determine window size, and set table accordingly.
|
// Determine window size, and set table accordingly.
|
||||||
int width = conf.get ("defaultwidth", 80);
|
int width = conf.get ("defaultwidth", 80);
|
||||||
|
@ -1957,7 +1972,7 @@ void handleReportActive (const TDB& tdb, T& task, Config& conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void handleReportOverdue (const TDB& tdb, T& task, Config& conf)
|
void handleReportOverdue (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Determine window size, and set table accordingly.
|
// Determine window size, and set table accordingly.
|
||||||
int width = conf.get ("defaultwidth", 80);
|
int width = conf.get ("defaultwidth", 80);
|
||||||
|
@ -2064,7 +2079,7 @@ void handleReportOverdue (const TDB& tdb, T& task, Config& conf)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Successively apply filters based on the task object built from the command
|
// Successively apply filters based on the task object built from the command
|
||||||
// line. Tasks that match all the specified criteria are listed.
|
// line. Tasks that match all the specified criteria are listed.
|
||||||
void handleReportOldest (const TDB& tdb, T& task, Config& conf)
|
void handleReportOldest (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Determine window size, and set table accordingly.
|
// Determine window size, and set table accordingly.
|
||||||
int width = conf.get ("defaultwidth", 80);
|
int width = conf.get ("defaultwidth", 80);
|
||||||
|
@ -2207,7 +2222,7 @@ void handleReportOldest (const TDB& tdb, T& task, Config& conf)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Successively apply filters based on the task object built from the command
|
// Successively apply filters based on the task object built from the command
|
||||||
// line. Tasks that match all the specified criteria are listed.
|
// line. Tasks that match all the specified criteria are listed.
|
||||||
void handleReportNewest (const TDB& tdb, T& task, Config& conf)
|
void handleReportNewest (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Determine window size, and set table accordingly.
|
// Determine window size, and set table accordingly.
|
||||||
int width = conf.get ("defaultwidth", 80);
|
int width = conf.get ("defaultwidth", 80);
|
||||||
|
@ -2350,7 +2365,7 @@ void handleReportNewest (const TDB& tdb, T& task, Config& conf)
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void handleReportStats (const TDB& tdb, T& task, Config& conf)
|
void handleReportStats (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
// Get all the tasks.
|
// Get all the tasks.
|
||||||
std::vector <T> tasks;
|
std::vector <T> tasks;
|
||||||
|
|
332
src/task.cpp
332
src/task.cpp
|
@ -345,7 +345,7 @@ int main (int argc, char** argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void nag (const TDB& tdb, T& task, Config& conf)
|
void nag (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
std::string nagMessage = conf.get ("nag", std::string (""));
|
std::string nagMessage = conf.get ("nag", std::string (""));
|
||||||
if (nagMessage != "")
|
if (nagMessage != "")
|
||||||
|
@ -390,138 +390,296 @@ int getDueState (const std::string& due)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Scan for recurring tasks, and generate any necessary instances of those
|
// Scans all tasks, and for any recurring tasks, determines whether any new
|
||||||
// tasks.
|
// child tasks need to be generated to fill gaps.
|
||||||
void handleRecurrence (const TDB& tdb, std::vector <T>& tasks)
|
void handleRecurrence (TDB& tdb, std::vector <T>& tasks)
|
||||||
{
|
{
|
||||||
std::vector <T> modified;
|
std::vector <T> modified;
|
||||||
Date now;
|
|
||||||
|
|
||||||
std::cout << "# handleRecurrence" << std::endl;
|
// Look at all tasks and find any recurring ones.
|
||||||
std::vector <T>::iterator it;
|
foreach (t, tasks)
|
||||||
for (it = tasks.begin (); it != tasks.end (); ++it)
|
|
||||||
{
|
{
|
||||||
if (it->getStatus () == T::recurring)
|
if (t->getStatus () == T::recurring)
|
||||||
{
|
{
|
||||||
std::cout << "# found recurring task " << it->getUUID () << std::endl;
|
// std::cout << "# found recurring task " << t->getUUID () << std::endl;
|
||||||
|
|
||||||
// This task is recurring. While it remains hidden from view, it spawns
|
// Generate a list of due dates for this recurring task, regardless of
|
||||||
// child tasks automatically, here, that are regular tasks, except they
|
// the mask.
|
||||||
// have a "parent" attribute that contains the UUID of the original.
|
std::vector <Date> due;
|
||||||
|
generateDueDates (*t, due);
|
||||||
|
|
||||||
// Generate a list of child tasks.
|
// Get the mask from the parent task.
|
||||||
std::vector <T> children;
|
std::string mask = t->getAttribute ("mask");
|
||||||
std::vector <T>::iterator them;
|
// std::cout << "# mask=" << mask << std::endl;
|
||||||
for (them = tasks.begin (); them != tasks.end (); ++them)
|
|
||||||
if (them->getAttribute ("parent") == it->getUUID ())
|
|
||||||
children.push_back (*them);
|
|
||||||
|
|
||||||
// Determine due date, recur period and until date.
|
// Iterate over the due dates, and check each against the mask.
|
||||||
Date due (atoi (it->getAttribute ("due").c_str ()));
|
bool changed = false;
|
||||||
std::cout << "# due=" << due.toString () << std::endl;
|
unsigned int i = 0;
|
||||||
std::string recur = it->getAttribute ("recur");
|
foreach (d, due)
|
||||||
std::cout << "# recur=" << recur << std::endl;
|
|
||||||
|
|
||||||
bool specificEnd = false;
|
|
||||||
Date until;
|
|
||||||
if (it->getAttribute ("until") != "")
|
|
||||||
{
|
{
|
||||||
until = Date (atoi (it->getAttribute ("until").c_str ()));
|
// std::cout << "# need: " << d->toString () << std::endl;
|
||||||
specificEnd = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "# specficEnd=" << (specificEnd ? "true" : "false") << std::endl;
|
if (mask.length () <= i)
|
||||||
if (specificEnd)
|
|
||||||
std::cout << "# until=" << until.toString () << std::endl;
|
|
||||||
|
|
||||||
for (Date i = due; ; i = getNextRecurrence (i, recur))
|
|
||||||
{
|
{
|
||||||
std::cout << "# i=" << i.toString () << std::endl;
|
mask += '-';
|
||||||
if (specificEnd && i > until)
|
changed = true;
|
||||||
break;
|
|
||||||
|
|
||||||
// Look to see if there is a gap at date "i" by scanning children.
|
T rec (*t); // Clone the parent.
|
||||||
bool foundChild = false;
|
rec.setId (tdb.nextId ()); // Assign a unique id.
|
||||||
std::vector <T>::iterator cit;
|
rec.setUUID (uuid ()); // New UUID.
|
||||||
for (cit = children.begin (); cit != children.end (); ++cit)
|
rec.setStatus (T::pending); // Shiny.
|
||||||
{
|
rec.setAttribute ("parent", t->getUUID ()); // Remember mom.
|
||||||
if (atoi (cit->getAttribute ("due").c_str ()) == i.toEpoch ())
|
|
||||||
{
|
|
||||||
foundChild = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO A gap may be filled by a completed task. Oh crap.
|
|
||||||
|
|
||||||
// There is a gap, so insert a task.
|
|
||||||
if (!foundChild)
|
|
||||||
{
|
|
||||||
std::cout << "# found a gap at i=" << i.toString () << std::endl;
|
|
||||||
T rec (*it); // Clone the parent.
|
|
||||||
|
|
||||||
char dueDate[16];
|
char dueDate[16];
|
||||||
sprintf (dueDate, "%u", (unsigned int) i.toEpoch ());
|
sprintf (dueDate, "%u", (unsigned int) d->toEpoch ());
|
||||||
rec.setAttribute ("due", dueDate);
|
rec.setAttribute ("due", dueDate); // Store generated due date.
|
||||||
rec.setAttribute ("parent", it->getUUID ());
|
|
||||||
rec.setStatus (T::pending);
|
|
||||||
|
|
||||||
std::cout << "# adding to modified" << std::endl;
|
char indexMask[12];
|
||||||
|
sprintf (indexMask, "%u", (unsigned int) i);
|
||||||
|
rec.setAttribute ("imask", indexMask); // Store index into mask.
|
||||||
|
|
||||||
|
// Add the new task to the vector, for immediate use.
|
||||||
|
// std::cout << "# adding to modified" << std::endl;
|
||||||
modified.push_back (rec);
|
modified.push_back (rec);
|
||||||
std::cout << "# adding to pending" << std::endl;
|
|
||||||
|
// Add the new task to the DB.
|
||||||
|
// std::cout << "# adding to pending" << std::endl;
|
||||||
tdb.addT (rec);
|
tdb.addT (rec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i > now)
|
++i;
|
||||||
{
|
|
||||||
std::cout << "# already 1 instance into the future, stopping" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only modify the parent if necessary.
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
// std::cout << "# modifying parent with mask=" << mask << std::endl;
|
||||||
|
t->setAttribute ("mask", mask);
|
||||||
|
tdb.modifyT (*t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
modified.push_back (*it);
|
modified.push_back (*t);
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks = modified;
|
tasks = modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Determine a start date (due), an optional end date (until), and an increment
|
||||||
|
// period (recur). Then generate a set of corresponding dates.
|
||||||
|
void generateDueDates (T& parent, std::vector <Date>& allDue)
|
||||||
|
{
|
||||||
|
// Determine due date, recur period and until date.
|
||||||
|
Date due (atoi (parent.getAttribute ("due").c_str ()));
|
||||||
|
// std::cout << "# due=" << due.toString () << std::endl;
|
||||||
|
std::string recur = parent.getAttribute ("recur");
|
||||||
|
// std::cout << "# recur=" << recur << std::endl;
|
||||||
|
|
||||||
|
bool specificEnd = false;
|
||||||
|
Date until;
|
||||||
|
if (parent.getAttribute ("until") != "")
|
||||||
|
{
|
||||||
|
until = Date (atoi (parent.getAttribute ("until").c_str ()));
|
||||||
|
specificEnd = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// std::cout << "# specficEnd=" << (specificEnd ? "true" : "false") << std::endl;
|
||||||
|
// if (specificEnd)
|
||||||
|
// std::cout << "# until=" << until.toString () << std::endl;
|
||||||
|
|
||||||
|
Date now;
|
||||||
|
for (Date i = due; ; i = getNextRecurrence (i, recur))
|
||||||
|
{
|
||||||
|
allDue.push_back (i);
|
||||||
|
|
||||||
|
// std::cout << "# i=" << i.toString () << std::endl;
|
||||||
|
if (specificEnd && i > until)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (i > now)
|
||||||
|
// {
|
||||||
|
// std::cout << "# already 1 instance into the future, stopping" << std::endl;
|
||||||
|
break;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
Date getNextRecurrence (Date& current, std::string& period)
|
Date getNextRecurrence (Date& current, std::string& period)
|
||||||
{
|
{
|
||||||
int days = convertDuration (period);
|
|
||||||
|
|
||||||
// Some periods are difficult, because they can be vague.
|
|
||||||
if (period == "monthly" ||
|
|
||||||
(isdigit (period[0]) && period[period.length () - 1] == 'm'))
|
|
||||||
{
|
|
||||||
int m = current.month ();
|
int m = current.month ();
|
||||||
int d = current.day ();
|
int d = current.day ();
|
||||||
int y = current.year ();
|
int y = current.year ();
|
||||||
|
|
||||||
if (++m == 13) m = 1;
|
// Some periods are difficult, because they can be vague.
|
||||||
|
if (period == "monthly")
|
||||||
|
{
|
||||||
|
if (++m > 12)
|
||||||
|
{
|
||||||
|
m -= 12;
|
||||||
|
++y;
|
||||||
|
}
|
||||||
|
|
||||||
while (! Date::valid (m, d, y))
|
while (! Date::valid (m, d, y))
|
||||||
--d;
|
--d;
|
||||||
|
|
||||||
std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
|
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
|
||||||
return Date (m, d, y);
|
return Date (m, d, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (period == "bimonthly" ||
|
if (isdigit (period[0]) && period[period.length () - 1] == 'm')
|
||||||
period == "semimonthly" ||
|
|
||||||
period == "quarterly" ||
|
|
||||||
period == "biannual" ||
|
|
||||||
period == "biyearly" ||
|
|
||||||
period == "semiannual" ||
|
|
||||||
(isdigit (period[0]) && (
|
|
||||||
period[period.length () - 1] == 'm' ||
|
|
||||||
period[period.length () - 1] == 'q')))
|
|
||||||
{
|
{
|
||||||
// TODO lots of work here...
|
std::string numeric = period.substr (0, period.length () - 1);
|
||||||
|
int increment = atoi (numeric.c_str ());
|
||||||
|
|
||||||
|
m += increment;
|
||||||
|
while (m > 12)
|
||||||
|
{
|
||||||
|
m -= 12;
|
||||||
|
++y;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (! Date::valid (m, d, y))
|
||||||
|
--d;
|
||||||
|
|
||||||
|
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
|
||||||
|
return Date (m, d, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (period == "quarterly")
|
||||||
|
{
|
||||||
|
m += 3;
|
||||||
|
if (m > 12)
|
||||||
|
{
|
||||||
|
m -= 12;
|
||||||
|
++y;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (! Date::valid (m, d, y))
|
||||||
|
--d;
|
||||||
|
|
||||||
|
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
|
||||||
|
return Date (m, d, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (isdigit (period[0]) && period[period.length () - 1] == 'q')
|
||||||
|
{
|
||||||
|
std::string numeric = period.substr (0, period.length () - 1);
|
||||||
|
int increment = atoi (numeric.c_str ());
|
||||||
|
|
||||||
|
m += 3 * increment;
|
||||||
|
while (m > 12)
|
||||||
|
{
|
||||||
|
m -= 12;
|
||||||
|
++y;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (! Date::valid (m, d, y))
|
||||||
|
--d;
|
||||||
|
|
||||||
|
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
|
||||||
|
return Date (m, d, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (period == "semiannual")
|
||||||
|
{
|
||||||
|
m += 6;
|
||||||
|
if (m > 12)
|
||||||
|
{
|
||||||
|
m -= 12;
|
||||||
|
++y;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (! Date::valid (m, d, y))
|
||||||
|
--d;
|
||||||
|
|
||||||
|
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
|
||||||
|
return Date (m, d, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (period == "bimonthly")
|
||||||
|
{
|
||||||
|
m += 2;
|
||||||
|
if (m > 12)
|
||||||
|
{
|
||||||
|
m -= 12;
|
||||||
|
++y;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (! Date::valid (m, d, y))
|
||||||
|
--d;
|
||||||
|
|
||||||
|
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
|
||||||
|
return Date (m, d, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (period == "biannual" ||
|
||||||
|
period == "biyearly")
|
||||||
|
{
|
||||||
|
y += 2;
|
||||||
|
|
||||||
|
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
|
||||||
|
return Date (m, d, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the period is an 'easy' one, add it to current, and we're done.
|
// If the period is an 'easy' one, add it to current, and we're done.
|
||||||
|
int days = convertDuration (period);
|
||||||
return current + (days * 86400);
|
return current + (days * 86400);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// When the status of a recurring child task changes, the parent task must
|
||||||
|
// update it's mask.
|
||||||
|
void updateRecurrenceMask (
|
||||||
|
TDB& tdb,
|
||||||
|
std::vector <T>& all,
|
||||||
|
T& task)
|
||||||
|
{
|
||||||
|
std::string parent = task.getAttribute ("parent");
|
||||||
|
// std::cout << "# updateRecurrenceMask of " << parent << std::endl;
|
||||||
|
if (parent != "")
|
||||||
|
{
|
||||||
|
std::vector <T>::iterator it;
|
||||||
|
for (it = all.begin (); it != all.end (); ++it)
|
||||||
|
{
|
||||||
|
if (it->getUUID () == parent)
|
||||||
|
{
|
||||||
|
// std::cout << "# located parent task" << std::endl;
|
||||||
|
unsigned int index = atoi (task.getAttribute ("imask").c_str ());
|
||||||
|
// std::cout << "# child imask=" << index << std::endl;
|
||||||
|
std::string mask = it->getAttribute ("mask");
|
||||||
|
// std::cout << "# parent mask=" << mask << std::endl;
|
||||||
|
if (mask.length () > index)
|
||||||
|
{
|
||||||
|
mask[index] = (task.getStatus () == T::pending) ? '-'
|
||||||
|
: (task.getStatus () == T::completed) ? '+'
|
||||||
|
: (task.getStatus () == T::deleted) ? 'X'
|
||||||
|
: '?';
|
||||||
|
|
||||||
|
// std::cout << "# setting parent mask to=" << mask << std::endl;
|
||||||
|
it->setAttribute ("mask", mask);
|
||||||
|
// std::cout << "# tdb.modifyT (parent)" << std::endl;
|
||||||
|
tdb.modifyT (*it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// std::cout << "# mask of insufficient length" << std::endl;
|
||||||
|
// std::cout << "# should never occur" << std::endl;
|
||||||
|
std::string mask;
|
||||||
|
for (unsigned int i = 0; i < index; ++i)
|
||||||
|
mask += "?";
|
||||||
|
|
||||||
|
mask += (task.getStatus () == T::pending) ? '-'
|
||||||
|
: (task.getStatus () == T::completed) ? '+'
|
||||||
|
: (task.getStatus () == T::deleted) ? 'X'
|
||||||
|
: '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
return; // No point continuing the loop.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
52
src/task.h
52
src/task.h
|
@ -59,42 +59,44 @@ bool validDate (std::string&, Config&);
|
||||||
|
|
||||||
// task.cpp
|
// task.cpp
|
||||||
void gatherNextTasks (const TDB&, T&, Config&, std::vector <T>&, std::vector <int>&);
|
void gatherNextTasks (const TDB&, T&, Config&, std::vector <T>&, std::vector <int>&);
|
||||||
void nag (const TDB&, T&, Config&);
|
void nag (TDB&, T&, Config&);
|
||||||
int getDueState (const std::string&);
|
int getDueState (const std::string&);
|
||||||
void handleRecurrence (const TDB&, std::vector <T>&);
|
void handleRecurrence (TDB&, std::vector <T>&);
|
||||||
|
void generateDueDates (T&, std::vector <Date>&);
|
||||||
Date getNextRecurrence (Date&, std::string&);
|
Date getNextRecurrence (Date&, std::string&);
|
||||||
|
void updateRecurrenceMask (TDB&, std::vector <T>&, T&);
|
||||||
|
|
||||||
// command.cpp
|
// command.cpp
|
||||||
void handleAdd (const TDB&, T&, Config&);
|
void handleAdd (const TDB&, T&, Config&);
|
||||||
void handleProjects (const TDB&, T&, Config&);
|
void handleProjects (TDB&, T&, Config&);
|
||||||
void handleTags (const TDB&, T&, Config&);
|
void handleTags (TDB&, T&, Config&);
|
||||||
void handleUndelete (const TDB&, T&, Config&);
|
void handleUndelete (TDB&, T&, Config&);
|
||||||
void handleVersion (Config&);
|
void handleVersion (Config&);
|
||||||
void handleExport (const TDB&, T&, Config&);
|
void handleExport (TDB&, T&, Config&);
|
||||||
void handleDelete (const TDB&, T&, Config&);
|
void handleDelete (TDB&, T&, Config&);
|
||||||
void handleStart (const TDB&, T&, Config&);
|
void handleStart (TDB&, T&, Config&);
|
||||||
void handleDone (const TDB&, T&, Config&);
|
void handleDone (TDB&, T&, Config&);
|
||||||
void handleModify (const TDB&, T&, Config&);
|
void handleModify (TDB&, T&, Config&);
|
||||||
void handleColor (Config&);
|
void handleColor (Config&);
|
||||||
|
|
||||||
// report.cpp
|
// report.cpp
|
||||||
void filter (std::vector<T>&, T&);
|
void filter (std::vector<T>&, T&);
|
||||||
void handleList (const TDB&, T&, Config&);
|
void handleList (TDB&, T&, Config&);
|
||||||
void handleInfo (const TDB&, T&, Config&);
|
void handleInfo (TDB&, T&, Config&);
|
||||||
void handleLongList (const TDB&, T&, Config&);
|
void handleLongList (TDB&, T&, Config&);
|
||||||
void handleSmallList (const TDB&, T&, Config&);
|
void handleSmallList (TDB&, T&, Config&);
|
||||||
void handleCompleted (const TDB&, T&, Config&);
|
void handleCompleted (TDB&, T&, Config&);
|
||||||
void handleReportSummary (const TDB&, T&, Config&);
|
void handleReportSummary (TDB&, T&, Config&);
|
||||||
void handleReportNext (const TDB&, T&, Config&);
|
void handleReportNext (TDB&, T&, Config&);
|
||||||
void handleReportHistory (const TDB&, T&, Config&);
|
void handleReportHistory (TDB&, T&, Config&);
|
||||||
void handleReportGHistory (const TDB&, T&, Config&);
|
void handleReportGHistory (TDB&, T&, Config&);
|
||||||
void handleReportUsage (const TDB&, T&, Config&);
|
void handleReportUsage (const TDB&, T&, Config&);
|
||||||
void handleReportCalendar (const TDB&, T&, Config&);
|
void handleReportCalendar (TDB&, T&, Config&);
|
||||||
void handleReportActive (const TDB&, T&, Config&);
|
void handleReportActive (TDB&, T&, Config&);
|
||||||
void handleReportOverdue (const TDB&, T&, Config&);
|
void handleReportOverdue (TDB&, T&, Config&);
|
||||||
void handleReportStats (const TDB&, T&, Config&);
|
void handleReportStats (TDB&, T&, Config&);
|
||||||
void handleReportOldest (const TDB&, T&, Config&);
|
void handleReportOldest (TDB&, T&, Config&);
|
||||||
void handleReportNewest (const TDB&, T&, Config&);
|
void handleReportNewest (TDB&, T&, Config&);
|
||||||
|
|
||||||
// util.cpp
|
// util.cpp
|
||||||
bool confirm (const std::string&);
|
bool confirm (const std::string&);
|
||||||
|
|
|
@ -252,7 +252,6 @@ int convertDuration (std::string& input)
|
||||||
supported.push_back ("fortnight");
|
supported.push_back ("fortnight");
|
||||||
supported.push_back ("monthly");
|
supported.push_back ("monthly");
|
||||||
supported.push_back ("bimonthly");
|
supported.push_back ("bimonthly");
|
||||||
supported.push_back ("semimonthly");
|
|
||||||
supported.push_back ("quarterly");
|
supported.push_back ("quarterly");
|
||||||
supported.push_back ("biannual");
|
supported.push_back ("biannual");
|
||||||
supported.push_back ("biyearly");
|
supported.push_back ("biyearly");
|
||||||
|
@ -268,7 +267,6 @@ int convertDuration (std::string& input)
|
||||||
if (found == "daily" || found == "day") return 1;
|
if (found == "daily" || found == "day") return 1;
|
||||||
else if (found == "weekly" || found == "sennight") return 7;
|
else if (found == "weekly" || found == "sennight") return 7;
|
||||||
else if (found == "biweekly" || found == "fortnight") return 14;
|
else if (found == "biweekly" || found == "fortnight") return 14;
|
||||||
else if (found == "semimonthly") return 15;
|
|
||||||
else if (found == "monthly") return 30;
|
else if (found == "monthly") return 30;
|
||||||
else if (found == "bimonthly") return 61;
|
else if (found == "bimonthly") return 61;
|
||||||
else if (found == "quarterly") return 91;
|
else if (found == "quarterly") return 91;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue