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 - #620 Fix installation of man pages from tarball
- #600 Add retag command to internal help - #600 Add retag command to internal help
(thanks to Stefan Herold) (thanks to Stefan Herold)

View file

@ -1,26 +1,47 @@
= timew-modify(1) = timew-modify(1)
== NAME == NAME
timew-modify - change start or end date of an interval timew-modify - change the range of an interval
== SYNOPSIS == SYNOPSIS
[verse] [verse]
*timew modify* (*start*|*end*) _<id>_ _<date>_ *timew modify* (*start*|*end*) _<id>_ _<date>_
*timew modify* range _<id>_ _<range>_
== DESCRIPTION == DESCRIPTION
The 'modify' command is used to change the start or end date of an interval. The 'modify' command is used to change range 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. 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 == 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 *Modify the end date of an interval*::
+
Then having selected '@3' as the interval you wish to modify: If the interval to be modified has the same date as today, it can be omitted:
+
$ timew modify end @3 2020-12-28T17:00:00 $ 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 == SEE ALSO
**timew-lengthen**(1), **timew-lengthen**(1),

View file

@ -293,6 +293,32 @@ std::vector <std::string> CLI::getWords () const
return words; 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. // Search for 'value' in _entities category, return canonicalized value.
bool CLI::canonicalize ( bool CLI::canonicalize (

View file

@ -69,6 +69,8 @@ public:
std::string getCommand () const; std::string getCommand () const;
bool getComplementaryHint (const std::string&, bool) const; bool getComplementaryHint (const std::string&, bool) const;
bool getHint(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 <int> getIds () const;
std::set <std::string> getTags () const; std::set <std::string> getTags () const;
std::string getAnnotation() const; std::string getAnnotation() const;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -24,105 +24,37 @@
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <IntervalFilterAllWithIds.h>
#include <cassert>
#include <commands.h> #include <commands.h>
#include <format.h> #include <format.h>
#include <timew.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int CmdModify ( int CmdModify (
const CLI& cli, CLI& cli,
Rules& rules, Rules& rules,
Database& database, Database& database,
Journal& journal) Journal& journal)
{ {
const bool verbose = rules.getBoolean ("verbose"); const auto subCommand = cli.findSubCommand (std::set<std::string> {"start", "end", "range"});
auto words = cli.getWords (); if (subCommand.empty())
enum {MODIFY_START, MODIFY_END} op = MODIFY_START;
if (words.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 (); throw format ("Command 'modify' has no subcommand '{1}' defined!", subCommand);
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;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

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 ( int CmdMove (
const CLI& cli, CLI& cli,
Rules& rules, Rules& rules,
Database& database, Database& database,
Journal& journal) Journal& journal)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -65,7 +65,7 @@ bool lightweightVersionCheck (int, const char**);
void initializeEntities (CLI&); void initializeEntities (CLI&);
void initializeDataJournalAndRules (const CLI&, Database&, Journal&, Rules&); void initializeDataJournalAndRules (const CLI&, Database&, Journal&, Rules&);
void initializeExtensions (CLI&, const Rules&, Extensions&); 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 // helper.cpp
Color summaryIntervalColor (const Rules&, const std::set <std::string>&); 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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)) expectedStart="{:%Y%m%dT%H%M%S}Z".format(one_hour_before_utc))
def test_modify_invalid_subcommand(self): def test_modify_invalid_subcommand(self):
"""Modify without (start|stop) subcommand""" """Modify with invalid subcommand"""
now_utc = datetime.now(timezone.utc) now_utc = datetime.now(timezone.utc)
one_hour_before_utc = now_utc - timedelta(hours=1) 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("start {:%Y-%m-%dT%H:%M:%S}Z".format(one_hour_before_utc))
self.t("stop") self.t("stop")
code, out, err = self.t.runError("modify @1 {:%Y-%m-%dT%H:%M:%S}Z".format(now_utc)) 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 command to modify", err) self.assertIn("Must specify start|end|range command to modify", err)
def test_modify_no_end_time(self): def test_modify_no_end_time(self):
"""Modify without a time to stop at""" """Modify without a time to stop at"""
@ -281,6 +281,36 @@ class TestModify(TestCase):
code, out, err = self.t.runError("modify start @2") code, out, err = self.t.runError("modify start @2")
self.assertIn("ID '@2' does not correspond to any tracking.", err) 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__": if __name__ == "__main__":
from simpletap import TAPTestRunner from simpletap import TAPTestRunner