Add the sub-command range to the modify command.

Additionally, this also makes the syntax of the `modify` command more flexible.
Before, the sub-command had to follow right after the `modify` command, now one can also write it with the id between command and sub-command:
```
$ timew modify @1 start 09:30
```
The `range` sub-command modifies both start and end time of the interval. It expects a range as parameter, e.g.
```
$ timew modify @3 range 08:15 - 13:37
```

Closes #627

Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
This commit is contained in:
Thomas Lauf 2024-11-07 14:03:53 +01:00 committed by Thomas Lauf
parent e5bd6f2be4
commit 576af3fd5f
38 changed files with 467 additions and 159 deletions

View file

@ -1,3 +1,4 @@
- #658 Add sub-command 'range' to command 'modify'
- #620 Fix installation of man pages from tarball
- #600 Add retag command to internal help
(thanks to Stefan Herold)

View file

@ -1,26 +1,47 @@
= timew-modify(1)
== NAME
timew-modify - change start or end date of an interval
timew-modify - change the range of an interval
== SYNOPSIS
[verse]
*timew modify* (*start*|*end*) _<id>_ _<date>_
*timew modify* range _<id>_ _<range>_
== DESCRIPTION
The 'modify' command is used to change the start or end date of an interval.
Using the 'summary' command, and specifying the ':ids' hint shows interval IDs.
Using the right ID, you can identify an interval to modify.
The 'modify' command is used to change range of an interval.
Using the 'start' or 'end' subcommand, one can either specify a new start or end date respectively, or with the 'range' subcommand, change the complete range.
The interval to be modified is specified via its id.
If the resulting interval overlaps with an existing interval, the command will return an error.
One can the ':adjust' hint to force an overwrite in this case.
See **timew-summary**(1) on how to retrieve the interval id.
== EXAMPLES
For example, show the IDs:
*Modify the start date of an interval*::
+
$ timew modify start @3 2020-12-28T17:00
+
This sets the start of interval '@3' to '17:00' of date '2020-12-28'.
If this datetime is after the end of the interval, the command will return an error.
$ timew summary :week :ids
Then having selected '@3' as the interval you wish to modify:
$ timew modify end @3 2020-12-28T17:00:00
*Modify the end date of an interval*::
+
If the interval to be modified has the same date as today, it can be omitted:
+
$ timew modify end @3 18:00
+
Similar to when modifying the interval start, the end datetime has to be after the start datetime.
*Modify the range of an interval*::
+
Instead of modifying start and end separately, those can be combined into a single call of the 'range' subcommand:
+
$ timew modify range @3 2020-12-28T17:00 - 2020-12-28T18:00
+
As in the examples above, the date portion can be omitted, if the date of the interval is today.
== SEE ALSO
**timew-lengthen**(1),

View file

@ -293,6 +293,32 @@ std::vector <std::string> CLI::getWords () const
return words;
}
////////////////////////////////////////////////////////////////////////////////
// Find and mark sub-command, return empty string if not found
std::string CLI::findSubCommand(const std::set<std::string>& subCommands) {
for (auto &a: _args) {
if (a.hasTag("BINARY") &&
a.hasTag("CMD") &&
a.hasTag("CONFIG") &&
a.hasTag("HINT")) {
continue;
}
if (subCommands.find(a.attribute("raw")) != subCommands.end()) {
a.tag ("CMD");
return a.attribute("raw");
}
}
return "";
}
////////////////////////////////////////////////////////////////////////////////
// Find and mark sub-command, return given default if not found
std::string CLI::getSubCommand(const std::set<std::string>& subCommands, const std::string& defaultCommand) {
const auto& subCommand = findSubCommand (subCommands);
return subCommand.empty () ? defaultCommand : subCommand;
}
////////////////////////////////////////////////////////////////////////////////
// Search for 'value' in _entities category, return canonicalized value.
bool CLI::canonicalize (

View file

@ -69,6 +69,8 @@ public:
std::string getCommand () const;
bool getComplementaryHint (const std::string&, bool) const;
bool getHint(const std::string&, bool) const;
std::string findSubCommand(const std::set<std::string>&);
std::string getSubCommand(const std::set<std::string>&, const std::string&);
std::set <int> getIds () const;
std::set <std::string> getTags () const;
std::string getAnnotation() const;

View file

@ -33,6 +33,9 @@ set (commands_SRCS CmdAnnotate.cpp
CmdJoin.cpp
CmdLengthen.cpp
CmdModify.cpp
CmdModifyEnd.cpp
CmdModifyRange.cpp
CmdModifyStart.cpp
CmdMove.cpp
CmdReport.cpp
CmdResize.cpp

View file

@ -34,7 +34,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdAnnotate (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -36,13 +36,13 @@
#include <iostream>
#include <timew.h>
int renderChart (const std::string&, const CLI&, Rules&, Database&);
int renderChart (const std::string&, CLI&, Rules&, Database&);
std::map <Datetime, std::string> createHolidayMap (Rules&, Range&);
////////////////////////////////////////////////////////////////////////////////
int CmdChartDay (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database)
{
@ -51,7 +51,7 @@ int CmdChartDay (
////////////////////////////////////////////////////////////////////////////////
int CmdChartWeek (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database)
{
@ -60,7 +60,7 @@ int CmdChartWeek (
////////////////////////////////////////////////////////////////////////////////
int CmdChartMonth (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database)
{
@ -70,7 +70,7 @@ int CmdChartMonth (
////////////////////////////////////////////////////////////////////////////////
int renderChart (
const std::string& type,
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database)
{

View file

@ -35,7 +35,7 @@
// timew config name Remove name
// timew config Show all config
int CmdConfig (
const CLI& cli,
CLI& cli,
Rules& rules,
Journal& journal)
{

View file

@ -36,7 +36,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdContinue (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -32,7 +32,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdDelete (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -34,7 +34,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdExport (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database)
{

View file

@ -33,7 +33,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdFill (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -32,7 +32,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdGaps (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database)
{

View file

@ -33,7 +33,7 @@
////////////////////////////////////////////////////////////////////////////////
// Іdentify DOM references in cli, provide space-separated results.
int CmdGet (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database)
{

View file

@ -52,6 +52,7 @@ int CmdHelpUsage (const Extensions& extensions)
<< " timew join @<id> @<id>\n"
<< " timew lengthen @<id> [@<id> ...] <duration>\n"
<< " timew modify (start|end) @<id> <date>\n"
<< " timew modify range @<id> <interval>\n"
<< " timew month [<interval>] [<tag> ...]\n"
<< " timew move @<id> <date>\n"
<< " timew [report] <report> [<interval>] [<tag> ...]\n"
@ -113,7 +114,7 @@ int CmdHelpUsage (const Extensions& extensions)
}
int CmdHelp (
const CLI& cli,
CLI& cli,
const Extensions& extensions)
{
auto words = cli.getWords ();

View file

@ -33,7 +33,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdJoin (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -33,7 +33,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdLengthen (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -24,105 +24,37 @@
//
////////////////////////////////////////////////////////////////////////////////
#include <IntervalFilterAllWithIds.h>
#include <cassert>
#include <commands.h>
#include <format.h>
#include <timew.h>
////////////////////////////////////////////////////////////////////////////////
int CmdModify (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)
{
const bool verbose = rules.getBoolean ("verbose");
const auto subCommand = cli.findSubCommand (std::set<std::string> {"start", "end", "range"});
auto words = cli.getWords ();
enum {MODIFY_START, MODIFY_END} op = MODIFY_START;
if (words.empty())
if (subCommand.empty())
{
throw std::string ("Must specify start|end command to modify. See 'timew help modify'.");
throw std::string ("Must specify start|end|range command to modify. See 'timew help modify'.");
}
if (words.at (0) == "start")
if (subCommand == "start")
{
op = MODIFY_START;
return CmdModifyStart (cli, rules, database, journal);
}
else if (words.at (0) == "end")
if (subCommand == "end")
{
op = MODIFY_END;
return CmdModifyEnd (cli, rules, database, journal);
}
else
if (subCommand == "range")
{
throw format ("Must specify start|end command to modify. See 'timew help modify'.", words.at (0));
return CmdModifyRange (cli, rules, database, journal);
}
auto ids = cli.getIds ();
if (ids.empty ())
{
throw std::string ("ID must be specified. See 'timew help modify'.");
}
if (ids.size () > 1)
{
throw std::string ("Only one ID may be specified. See 'timew help modify'.");
}
auto range = cli.getRange ({0, 0});
int id = *ids.begin();
flattenDatabase (database, rules);
auto filtering = IntervalFilterAllWithIds (ids);
auto intervals = getTracked (database, rules, filtering);
if (intervals.empty())
{
throw format ("ID '@{1}' does not correspond to any tracking.", id);
}
assert (intervals.size () == 1);
if (! range.is_started ())
{
throw std::string ("No updated time specified. See 'timew help modify'.");
}
const Interval interval = intervals.at (0);
Interval modified {interval};
switch (op)
{
case MODIFY_START:
modified.start = range.start;
break;
case MODIFY_END:
if (interval.is_open ())
{
throw format ("Cannot modify end of open interval @{1}.", id);
}
modified.end = range.start;
break;
}
if (! modified.is_open () && (modified.start > modified.end))
{
throw format ("Cannot modify interval @{1} where start is after end.", id);
}
journal.startTransaction ();
database.deleteInterval (interval);
validate (cli, rules, database, modified);
database.addInterval (modified, verbose);
journal.endTransaction();
return 0;
throw format ("Command 'modify' has no subcommand '{1}' defined!", subCommand);
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,97 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2024, Thomas Lauf, 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 <IntervalFilterAllWithIds.h>
#include <cassert>
#include <commands.h>
#include <format.h>
#include <timew.h>
////////////////////////////////////////////////////////////////////////////////
int CmdModifyEnd (
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)
{
const bool verbose = rules.getBoolean ("verbose");
auto ids = cli.getIds ();
if (ids.empty ())
{
throw std::string ("ID must be specified. See 'timew help modify'.");
}
if (ids.size () > 1)
{
throw std::string ("Only one ID may be specified. See 'timew help modify'.");
}
auto range = cli.getRange ({0, 0});
int id = *ids.begin();
flattenDatabase (database, rules);
auto filtering = IntervalFilterAllWithIds (ids);
auto intervals = getTracked (database, rules, filtering);
if (intervals.empty())
{
throw format ("ID '@{1}' does not correspond to any tracking.", id);
}
assert (intervals.size () == 1);
if (! range.is_started ())
{
throw std::string ("No updated time specified. See 'timew help modify'.");
}
const Interval interval = intervals.at (0);
Interval modified {interval};
if (interval.is_open ())
{
throw format ("Cannot modify end of open interval @{1}.", id);
}
modified.end = range.start;
if (! modified.is_open () && (modified.start > modified.end))
{
throw format ("Cannot modify interval @{1} where start is after end.", id);
}
journal.startTransaction ();
database.deleteInterval (interval);
validate (cli, rules, database, modified);
database.addInterval (modified, verbose);
journal.endTransaction();
return 0;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,99 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2024, Thomas Lauf, 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 <IntervalFilterAllWithIds.h>
#include <cassert>
#include <commands.h>
#include <format.h>
#include <timew.h>
////////////////////////////////////////////////////////////////////////////////
int CmdModifyRange (
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)
{
const bool verbose = rules.getBoolean ("verbose");
auto ids = cli.getIds ();
if (ids.empty ())
{
throw std::string ("ID must be specified. See 'timew help modify'.");
}
if (ids.size () > 1)
{
throw std::string ("Only one ID may be specified. See 'timew help modify'.");
}
auto range = cli.getRange ({0, 0});
if (!range.is_open() && range.start > range.end)
{
throw format ("Invalid range where start is after end given! {1}", range.dump ());
}
int id = *ids.begin();
flattenDatabase (database, rules);
auto filtering = IntervalFilterAllWithIds (ids);
auto intervals = getTracked (database, rules, filtering);
if (intervals.empty())
{
throw format ("ID '@{1}' does not correspond to any tracking.", id);
}
assert (intervals.size () == 1);
if (! range.is_started ())
{
throw std::string ("No updated time specified. See 'timew help modify'.");
}
const Interval interval = intervals.at (0);
Interval modified {interval};
modified.start = range.start;
modified.end = range.end;
if (! modified.is_open () && (modified.start > modified.end))
{
throw format ("Cannot modify interval @{1} where start is after end.", id);
}
journal.startTransaction ();
database.deleteInterval (interval);
validate (cli, rules, database, modified);
database.addInterval (modified, verbose);
journal.endTransaction();
return 0;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,93 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2024, Thomas Lauf, 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 <IntervalFilterAllWithIds.h>
#include <cassert>
#include <commands.h>
#include <format.h>
#include <timew.h>
////////////////////////////////////////////////////////////////////////////////
int CmdModifyStart (
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)
{
const bool verbose = rules.getBoolean ("verbose");
auto ids = cli.getIds ();
if (ids.empty ())
{
throw std::string ("ID must be specified. See 'timew help modify'.");
}
if (ids.size () > 1)
{
throw std::string ("Only one ID may be specified. See 'timew help modify'.");
}
auto range = cli.getRange ({0, 0});
int id = *ids.begin();
flattenDatabase (database, rules);
auto filtering = IntervalFilterAllWithIds (ids);
auto intervals = getTracked (database, rules, filtering);
if (intervals.empty())
{
throw format ("ID '@{1}' does not correspond to any tracking.", id);
}
assert (intervals.size () == 1);
if (! range.is_started ())
{
throw std::string ("No updated time specified. See 'timew help modify'.");
}
const Interval interval = intervals.at (0);
Interval modified {interval};
modified.start = range.start;
if (! modified.is_open () && (modified.start > modified.end))
{
throw format ("Cannot modify interval @{1} where start is after end.", id);
}
journal.startTransaction ();
database.deleteInterval (interval);
validate (cli, rules, database, modified);
database.addInterval (modified, verbose);
journal.endTransaction();
return 0;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -33,7 +33,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdMove (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -107,7 +107,7 @@ std::string getScriptName (const std::string& script_path)
////////////////////////////////////////////////////////////////////////////////
int CmdReport (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
const Extensions& extensions)

View file

@ -33,7 +33,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdResize (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -34,7 +34,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdRetag (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -33,7 +33,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdShorten (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -33,7 +33,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdSplit (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -30,7 +30,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdStart (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -42,7 +42,7 @@ template <class T> T setIntersect (
////////////////////////////////////////////////////////////////////////////////
int CmdStop (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -42,7 +42,7 @@ std::string renderHolidays (const std::map <Datetime, std::string>&);
////////////////////////////////////////////////////////////////////////////////
int CmdSummary (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database)
{

View file

@ -34,7 +34,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdTag (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -38,7 +38,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdTags (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database)
{

View file

@ -30,7 +30,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdTrack (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -34,7 +34,7 @@
////////////////////////////////////////////////////////////////////////////////
int CmdUntag (
const CLI& cli,
CLI& cli,
Rules& rules,
Database& database,
Journal& journal)

View file

@ -33,41 +33,44 @@
#include <Journal.h>
#include <Rules.h>
int CmdAnnotate (const CLI&, Rules&, Database&, Journal& );
int CmdCancel ( Rules&, Database&, Journal& );
int CmdConfig (const CLI&, Rules&, Journal& );
int CmdContinue (const CLI&, Rules&, Database&, Journal& );
int CmdDefault ( Rules&, Database& );
int CmdDelete (const CLI&, Rules&, Database&, Journal& );
int CmdDiagnostics ( Rules&, Database&, const Extensions&);
int CmdExport (const CLI&, Rules&, Database& );
int CmdExtensions ( const Extensions&);
int CmdFill (const CLI&, Rules&, Database&, Journal& );
int CmdGaps (const CLI&, Rules&, Database& );
int CmdGet (const CLI&, Rules&, Database& );
int CmdHelpUsage ( const Extensions&);
int CmdHelp (const CLI&, const Extensions&);
int CmdJoin (const CLI&, Rules&, Database&, Journal& );
int CmdLengthen (const CLI&, Rules&, Database&, Journal& );
int CmdModify (const CLI&, Rules&, Database&, Journal& );
int CmdMove (const CLI&, Rules&, Database&, Journal& );
int CmdReport (const CLI&, Rules&, Database&, const Extensions&);
int CmdResize (const CLI&, Rules&, Database&, Journal& );
int CmdRetag (const CLI&, Rules&, Database&, Journal& );
int CmdShorten (const CLI&, Rules&, Database&, Journal& );
int CmdShow ( Rules& );
int CmdSplit (const CLI&, Rules&, Database&, Journal& );
int CmdStart (const CLI&, Rules&, Database&, Journal& );
int CmdStop (const CLI&, Rules&, Database&, Journal& );
int CmdTag (const CLI&, Rules&, Database&, Journal& );
int CmdTags (const CLI&, Rules&, Database& );
int CmdTrack (const CLI&, Rules&, Database&, Journal& );
int CmdUndo ( Rules&, Database&, Journal& );
int CmdUntag (const CLI&, Rules&, Database&, Journal& );
int CmdAnnotate (CLI&, Rules&, Database&, Journal& );
int CmdCancel ( Rules&, Database&, Journal& );
int CmdConfig (CLI&, Rules&, Journal& );
int CmdContinue (CLI&, Rules&, Database&, Journal& );
int CmdDefault ( Rules&, Database& );
int CmdDelete (CLI&, Rules&, Database&, Journal& );
int CmdDiagnostics ( Rules&, Database&, const Extensions&);
int CmdExport (CLI&, Rules&, Database& );
int CmdExtensions ( const Extensions&);
int CmdFill (CLI&, Rules&, Database&, Journal& );
int CmdGaps (CLI&, Rules&, Database& );
int CmdGet (CLI&, Rules&, Database& );
int CmdHelpUsage ( const Extensions&);
int CmdHelp (CLI&, const Extensions&);
int CmdJoin (CLI&, Rules&, Database&, Journal& );
int CmdLengthen (CLI&, Rules&, Database&, Journal& );
int CmdModify (CLI&, Rules&, Database&, Journal& );
int CmdModifyEnd (CLI&, Rules&, Database&, Journal& );
int CmdModifyRange (CLI&, Rules&, Database&, Journal& );
int CmdModifyStart (CLI&, Rules&, Database&, Journal& );
int CmdMove (CLI&, Rules&, Database&, Journal& );
int CmdReport (CLI&, Rules&, Database&, const Extensions&);
int CmdResize (CLI&, Rules&, Database&, Journal& );
int CmdRetag (CLI&, Rules&, Database&, Journal& );
int CmdShorten (CLI&, Rules&, Database&, Journal& );
int CmdShow ( Rules& );
int CmdSplit (CLI&, Rules&, Database&, Journal& );
int CmdStart (CLI&, Rules&, Database&, Journal& );
int CmdStop (CLI&, Rules&, Database&, Journal& );
int CmdTag (CLI&, Rules&, Database&, Journal& );
int CmdTags (CLI&, Rules&, Database& );
int CmdTrack (CLI&, Rules&, Database&, Journal& );
int CmdUndo ( Rules&, Database&, Journal& );
int CmdUntag (CLI&, Rules&, Database&, Journal& );
int CmdChartDay (const CLI&, Rules&, Database& );
int CmdChartWeek (const CLI&, Rules&, Database& );
int CmdChartMonth (const CLI&, Rules&, Database& );
int CmdSummary (const CLI&, Rules&, Database& );
int CmdChartDay (CLI&, Rules&, Database& );
int CmdChartWeek (CLI&, Rules&, Database& );
int CmdChartMonth (CLI&, Rules&, Database& );
int CmdSummary (CLI&, Rules&, Database& );
#endif

View file

@ -200,7 +200,7 @@ void initializeExtensions (
////////////////////////////////////////////////////////////////////////////////
int dispatchCommand (
const CLI& cli,
CLI& cli,
Database& database,
Journal& journal,
Rules& rules,

View file

@ -65,7 +65,7 @@ bool lightweightVersionCheck (int, const char**);
void initializeEntities (CLI&);
void initializeDataJournalAndRules (const CLI&, Database&, Journal&, Rules&);
void initializeExtensions (CLI&, const Rules&, Extensions&);
int dispatchCommand (const CLI&, Database&, Journal&, Rules&, const Extensions&);
int dispatchCommand (CLI&, Database&, Journal&, Rules&, const Extensions&);
// helper.cpp
Color summaryIntervalColor (const Rules&, const std::set <std::string>&);

View file

@ -2,7 +2,7 @@
###############################################################################
#
# Copyright 2018 - 2022, Thomas Lauf, Paul Beckingham, Federico Hernandez.
# Copyright 2018 - 2022, 2024 Thomas Lauf, 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
@ -65,14 +65,14 @@ class TestModify(TestCase):
expectedStart="{:%Y%m%dT%H%M%S}Z".format(one_hour_before_utc))
def test_modify_invalid_subcommand(self):
"""Modify without (start|stop) subcommand"""
"""Modify with invalid subcommand"""
now_utc = datetime.now(timezone.utc)
one_hour_before_utc = now_utc - timedelta(hours=1)
self.t("start {:%Y-%m-%dT%H:%M:%S}Z".format(one_hour_before_utc))
self.t("stop")
code, out, err = self.t.runError("modify @1 {:%Y-%m-%dT%H:%M:%S}Z".format(now_utc))
self.assertIn("Must specify start|end command to modify", err)
code, out, err = self.t.runError("modify @1 bogus {:%Y-%m-%dT%H:%M:%S}Z".format(now_utc))
self.assertIn("Must specify start|end|range command to modify", err)
def test_modify_no_end_time(self):
"""Modify without a time to stop at"""
@ -281,6 +281,36 @@ class TestModify(TestCase):
code, out, err = self.t.runError("modify start @2")
self.assertIn("ID '@2' does not correspond to any tracking.", err)
def test_modify_range(self):
"""Call modify with range subcommand"""
now = datetime.now().replace(second=0, microsecond=0, minute=0)
now_utc = now.replace(tzinfo=tz.tzlocal()).astimezone(timezone.utc).replace(second=0, microsecond=0, minute=0)
two_hours_before_utc = now_utc - timedelta(hours=2)
three_hours_before_utc = now_utc - timedelta(hours=3)
four_hours_before_utc = now_utc - timedelta(hours=4)
self.t("track from {:%Y-%m-%dT%H:%M:%S}Z for 30min bar".format(four_hours_before_utc))
self.t("modify @1 range {:%Y-%m-%dT%H:%M:%S}Z - {:%Y-%m-%dT%H:%M:%S}Z".format(three_hours_before_utc, two_hours_before_utc))
j = self.t.export()
self.assertEqual(len(j), 1)
self.assertClosedInterval(j[0],
expectedStart=three_hours_before_utc,
expectedEnd=two_hours_before_utc,
expectedTags=['bar'])
def test_modify_range_with_point_in_time(self):
"""Call modify range with a point in time is an error"""
now = datetime.now().replace(second=0, microsecond=0, minute=0)
now_utc = now.replace(tzinfo=tz.tzlocal()).astimezone(timezone.utc).replace(second=0, microsecond=0, minute=0)
three_hours_before_utc = now_utc - timedelta(hours=3)
four_hours_before_utc = now_utc - timedelta(hours=4)
self.t("track from {:%Y-%m-%dT%H:%M:%S}Z for 30min bar".format(four_hours_before_utc))
self.t("modify @1 range {:%Y-%m-%dT%H:%M:%S}Z".format(three_hours_before_utc))
if __name__ == "__main__":
from simpletap import TAPTestRunner