mirror of
https://github.com/GothenburgBitFactory/timewarrior.git
synced 2025-06-26 10:54:28 +02:00
Add import command
Some checks failed
tests / tests (alpine-edge, Alpine Edge, ubuntu-latest) (push) Has been cancelled
tests / tests (alpine-latest, Alpine Latest, ubuntu-latest) (push) Has been cancelled
tests / tests (archlinux, Archlinux Base, ubuntu-latest) (push) Has been cancelled
tests / tests (centos-stream9, Centos Stream9, ubuntu-latest) (push) Has been cancelled
tests / tests (debianstable, Debian Stable, ubuntu-latest) (push) Has been cancelled
tests / tests (debiantesting, Debian Testing, ubuntu-latest) (push) Has been cancelled
tests / tests (fedora41, Fedora 41, ubuntu-latest) (push) Has been cancelled
tests / tests (fedora42, Fedora 42, ubuntu-latest) (push) Has been cancelled
tests / tests (opensuseleap, OpenSUSE Leap, ubuntu-latest) (push) Has been cancelled
tests / tests (opensusetumbleweed, OpenSUSE Tumbleweed, ubuntu-latest) (push) Has been cancelled
tests / tests (osx-13, macOS 13, macos-13) (push) Has been cancelled
tests / tests (osx-14, macOS 14, macos-14) (push) Has been cancelled
tests / tests (osx-15, macOS 15, macos-15) (push) Has been cancelled
tests / tests (ubuntu2204, Ubuntu 22.04, ubuntu-latest) (push) Has been cancelled
tests / tests (ubuntu2204, Ubuntu 24.04, ubuntu-latest) (push) Has been cancelled
Some checks failed
tests / tests (alpine-edge, Alpine Edge, ubuntu-latest) (push) Has been cancelled
tests / tests (alpine-latest, Alpine Latest, ubuntu-latest) (push) Has been cancelled
tests / tests (archlinux, Archlinux Base, ubuntu-latest) (push) Has been cancelled
tests / tests (centos-stream9, Centos Stream9, ubuntu-latest) (push) Has been cancelled
tests / tests (debianstable, Debian Stable, ubuntu-latest) (push) Has been cancelled
tests / tests (debiantesting, Debian Testing, ubuntu-latest) (push) Has been cancelled
tests / tests (fedora41, Fedora 41, ubuntu-latest) (push) Has been cancelled
tests / tests (fedora42, Fedora 42, ubuntu-latest) (push) Has been cancelled
tests / tests (opensuseleap, OpenSUSE Leap, ubuntu-latest) (push) Has been cancelled
tests / tests (opensusetumbleweed, OpenSUSE Tumbleweed, ubuntu-latest) (push) Has been cancelled
tests / tests (osx-13, macOS 13, macos-13) (push) Has been cancelled
tests / tests (osx-14, macOS 14, macos-14) (push) Has been cancelled
tests / tests (osx-15, macOS 15, macos-15) (push) Has been cancelled
tests / tests (ubuntu2204, Ubuntu 22.04, ubuntu-latest) (push) Has been cancelled
tests / tests (ubuntu2204, Ubuntu 24.04, ubuntu-latest) (push) Has been cancelled
The import command lets the user import time tracking data as produced by the export command, i.e. a JSON array of interval objects. Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
This commit is contained in:
parent
d925553116
commit
b3d9562c54
6 changed files with 170 additions and 0 deletions
|
@ -1,3 +1,4 @@
|
|||
- #676 Add import command
|
||||
- #677 Extension names starting with 'timew' cause problems
|
||||
(thanks to ftambara)
|
||||
- #661 Make display of ids and annotations the default in summary report for new users
|
||||
|
|
45
doc/man1/timew-import.1.adoc
Normal file
45
doc/man1/timew-import.1.adoc
Normal file
|
@ -0,0 +1,45 @@
|
|||
= timew-import(1)
|
||||
|
||||
== NAME
|
||||
timew-import - import time-tracking data from files
|
||||
|
||||
== SYNOPSIS
|
||||
[verse]
|
||||
*timew import* _<file>_**...**
|
||||
|
||||
== DESCRIPTION
|
||||
Import tracked time from `file`.
|
||||
|
||||
The files have to be in JSON format, as exported by **timew-export**(1), i.e. a single array with interval objects.
|
||||
|
||||
When importing, the intervals are checked for overlaps with existing intervals.
|
||||
If an overlap is found, the import will abort at the first overlap and no more intervals are imported, unless the `:adjust` hint is specified.
|
||||
|
||||
In general, it is recommended to create a backup of your data before importing.
|
||||
|
||||
== HINTS
|
||||
|
||||
**:adjust**::
|
||||
When given, the imported interval will overwrite any existing intervals that it overlaps with.
|
||||
|
||||
== EXAMPLES
|
||||
|
||||
*Import intervals from a file*::
|
||||
+
|
||||
Import intervals from a single file:
|
||||
+
|
||||
[source]
|
||||
----
|
||||
timew import intervals.json
|
||||
----
|
||||
+
|
||||
Any file path that does not start with a `/` is interpreted as relative to the current working directory.
|
||||
|
||||
*Import intervals from multiple files*::
|
||||
+
|
||||
One can also use shell wildcards and absolute paths:
|
||||
+
|
||||
[source]
|
||||
----
|
||||
timew import /path/to/intervals/*.json
|
||||
----
|
|
@ -30,6 +30,7 @@ set (commands_SRCS CmdAnnotate.cpp
|
|||
CmdGaps.cpp
|
||||
CmdGet.cpp
|
||||
CmdHelp.cpp
|
||||
CmdImport.cpp
|
||||
CmdJoin.cpp
|
||||
CmdLengthen.cpp
|
||||
CmdModify.cpp
|
||||
|
|
120
src/commands/CmdImport.cpp
Normal file
120
src/commands/CmdImport.cpp
Normal file
|
@ -0,0 +1,120 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2025, Gothenburg Bit Factory.
|
||||
//
|
||||
// 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 <commands.h>
|
||||
#include <format.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <timew.h>
|
||||
|
||||
#include <IntervalFactory.h>
|
||||
#include <JSON.h>
|
||||
|
||||
std::vector <Interval> import_file (const std::string &file_name);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int CmdImport(
|
||||
CLI &cli,
|
||||
Rules &rules,
|
||||
Database &database,
|
||||
Journal &journal) {
|
||||
const bool verbose = rules.getBoolean("verbose");
|
||||
|
||||
auto fileNames = cli.getWords();
|
||||
|
||||
for (const auto& fileName: fileNames)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto intervals = import_file (fileName);
|
||||
|
||||
journal.startTransaction ();
|
||||
for (auto& interval: intervals)
|
||||
{
|
||||
// Add each interval to the database
|
||||
if (validate (cli, rules, database, interval)) {
|
||||
database.addInterval (interval, verbose);
|
||||
database.commit ();
|
||||
}
|
||||
}
|
||||
journal.endTransaction ();
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
std::cout << "Imported " << intervals.size () << " interval(s) from '" << fileName << "'." << std::endl;
|
||||
}
|
||||
}
|
||||
catch (const std::string &error)
|
||||
{
|
||||
throw format("Error importing '{1}': {2}", fileName, error);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::vector<Interval> import_file (const std::string& file_name) {
|
||||
Path file_path;
|
||||
|
||||
if (file_name.at (0) == '/')
|
||||
{
|
||||
file_path = file_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
file_path = Directory::cwd() + "/" + file_name;
|
||||
}
|
||||
|
||||
std::string content;
|
||||
|
||||
if (const bool exists = file_path.exists(); exists && File::read(file_path, content))
|
||||
{
|
||||
std::unique_ptr<json::value> json(json::parse(content));
|
||||
|
||||
if (content.empty() || (json == nullptr))
|
||||
{
|
||||
throw std::string("Contents invalid.");
|
||||
}
|
||||
|
||||
if (json->type() != json::j_array)
|
||||
{
|
||||
throw std::string("Expected JSON array for import data.");
|
||||
}
|
||||
|
||||
std::vector<Interval> intervals;
|
||||
|
||||
for (auto item: dynamic_cast<json::array *>(json.get())->_data)
|
||||
{
|
||||
Interval new_interval = IntervalFactory::fromJson(item->dump());
|
||||
intervals.push_back(new_interval);
|
||||
}
|
||||
|
||||
return intervals;
|
||||
}
|
||||
|
||||
throw format("File {1} does not exist or cannot be read: ", file_name);
|
||||
}
|
|
@ -47,6 +47,7 @@ int CmdGaps (CLI&, Rules&, Database& );
|
|||
int CmdGet (CLI&, Rules&, Database& );
|
||||
int CmdHelpUsage ( const Extensions&);
|
||||
int CmdHelp (CLI&, const Extensions&);
|
||||
int CmdImport (CLI&, Rules&, Database&, Journal& );
|
||||
int CmdJoin (CLI&, Rules&, Database&, Journal& );
|
||||
int CmdLengthen (CLI&, Rules&, Database&, Journal& );
|
||||
int CmdModify (CLI&, Rules&, Database&, Journal& );
|
||||
|
|
|
@ -63,6 +63,7 @@ void initializeEntities (CLI& cli)
|
|||
cli.entity ("command", "help");
|
||||
cli.entity ("command", "--help");
|
||||
cli.entity ("command", "-h");
|
||||
cli.entity ("command", "import");
|
||||
cli.entity ("command", "join");
|
||||
cli.entity ("command", "lengthen");
|
||||
cli.entity ("command", "modify");
|
||||
|
@ -235,6 +236,7 @@ int dispatchCommand (
|
|||
else if (command == "help" ||
|
||||
command == "--help" ||
|
||||
command == "-h") status = CmdHelp (cli, extensions);
|
||||
else if (command == "import") status = CmdImport (cli, rules, database, journal );
|
||||
else if (command == "join") status = CmdJoin (cli, rules, database, journal );
|
||||
else if (command == "lengthen") status = CmdLengthen (cli, rules, database, journal );
|
||||
else if (command == "modify") status = CmdModify (cli, rules, database, journal );
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue