Compare commits

...

18 commits

Author SHA1 Message Date
Thomas Lauf
d925553116 Prevent the CLI to canonicalize the binary
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
For one, it is not necessary: the first argument on the command line is not used any
further when executing the command line. Also, if there is an extension that starts
with the same string as the binary, the binary gets also marked as an extension.
This in turn prohibits the detection of any other Timewarrior command such that e.g.
the command 'summary' in 'timew summary' is treated as an external report, which is
not present and thus makes the call fail.

Closes #677

Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
2025-06-25 19:07:42 +02:00
dependabot[bot]
d43e962c6c Bump sigstore/cosign-installer from 3.8.2 to 3.9.1
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
Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.8.2 to 3.9.1.
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](https://github.com/sigstore/cosign-installer/compare/v3.8.2...v3.9.1)

---
updated-dependencies:
- dependency-name: sigstore/cosign-installer
  dependency-version: 3.9.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-24 18:16:18 +02:00
Thomas Lauf
ae06f0526a Update download link for package downloads
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
2025-06-09 22:21:53 +02:00
dependabot[bot]
1fd82ec137 Bump docker/build-push-action from 6.17.0 to 6.18.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.17.0 to 6.18.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.17.0...v6.18.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 6.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-04 22:14:28 +02:00
Thomas Lauf
1d3dd8f440 Make display of ids and annotations the default in summary report for new users
Initialize a new 'timewarrior.cfg' file with the respective settings

Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
2025-06-01 23:39:53 +02:00
Thomas Lauf
191ad1ec8a Update test zoo docker images
* Add Fedora 42
* Remove Fedora 40
* Remove Ubuntu 20.04

Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
2025-05-31 21:43:31 +02:00
Thomas Lauf
817023f75d Update libshared to 121f757c3ec1b1f548f7835208b8c72d85d141a7
Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
2025-05-30 23:55:42 +02:00
Thomas Lauf
9b91a7ac71
Upgrade test infrastructure (#670)
* Drop Ubuntu 20.04 test runner
* Add macOS 15 test runner

Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
2025-05-30 23:42:48 +02:00
Thomas Lauf
331433c688 Update ChangeLog
Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
2025-05-30 22:51:01 +02:00
Thomas Lauf
09ed1d1e22 Align id filtering of export command
Align code structure of CmdChart, CmdExport, CmdReport, CmdSummary

Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
2025-05-30 22:51:01 +02:00
Thomas Lauf
6c74f7aa3c Add id filtering to reports
Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
2025-05-30 22:51:01 +02:00
Thomas Lauf
9cc2844595 Add id filtering to charts
Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
2025-05-30 22:51:01 +02:00
dependabot[bot]
ad838354a1 Bump docker/build-push-action from 6.16.0 to 6.17.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.16.0 to 6.17.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.16.0...v6.17.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 6.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-21 08:15:39 +02:00
Karam Abu Judom
dafced21ef fix typo in URL in README.md 2025-05-05 20:41:28 +02:00
dependabot[bot]
7dc7f54d3c Bump docker/build-push-action from 6.15.0 to 6.16.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.15.0 to 6.16.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.15.0...v6.16.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 6.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-29 20:53:40 +02:00
dependabot[bot]
7158687398 Bump sigstore/cosign-installer from 3.8.1 to 3.8.2
Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.8.1 to 3.8.2.
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](https://github.com/sigstore/cosign-installer/compare/v3.8.1...v3.8.2)

---
updated-dependencies:
- dependency-name: sigstore/cosign-installer
  dependency-version: 3.8.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-29 20:45:20 +02:00
Thomas Lauf
f36ad01788 Fix man page section numbers and reference formatting
Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
2025-04-26 19:49:10 +02:00
Thomas Lauf
e092041a38 Enter commit hash for release 1.8.0
Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
2025-04-20 21:38:42 +02:00
27 changed files with 270 additions and 191 deletions

View file

@ -33,7 +33,7 @@ jobs:
submodules: "recursive"
- name: Install cosign
uses: sigstore/cosign-installer@v3.8.1
uses: sigstore/cosign-installer@v3.9.1
- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3.4.0
@ -44,7 +44,7 @@ jobs:
- name: Build and push Timewarrior Docker image
id: build-and-push
uses: docker/build-push-action@v6.15.0
uses: docker/build-push-action@v6.18.0
with:
context: .
file: "./docker/timew.dockerfile"

View file

@ -24,21 +24,18 @@ jobs:
- name: "Debian Testing"
runner: ubuntu-latest
container: debiantesting
- name: "Fedora 40"
runner: ubuntu-latest
container: fedora40
- name: "Fedora 41"
runner: ubuntu-latest
container: fedora41
- name: "Fedora 42"
runner: ubuntu-latest
container: fedora42
- name: "OpenSUSE Leap"
runner: ubuntu-latest
container: opensuseleap
- name: "OpenSUSE Tumbleweed"
runner: ubuntu-latest
container: opensusetumbleweed
- name: "Ubuntu 20.04"
runner: ubuntu-latest
container: ubuntu2004
- name: "Ubuntu 22.04"
runner: ubuntu-latest
container: ubuntu2204
@ -51,6 +48,9 @@ jobs:
- name: "macOS 14"
runner: macos-14
container: osx-14
- name: "macOS 15"
runner: macos-15
container: osx-15
runs-on: ${{ matrix.runner }}
continue-on-error: ${{ matrix.continue-on-error == true }}
steps:

View file

@ -129,3 +129,4 @@ Thanks to the following, who submitted detailed bug reports and excellent sugges
Eugene Morozov
Stefan Herold
Sebastian Carlos
ftambara

View file

@ -7,7 +7,7 @@ project (timew)
include (CXXSniffer)
include (FindAsciidoctor)
set (PROJECT_VERSION "1.8.0")
set (PROJECT_VERSION "1.8.0-dev")
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)

View file

@ -1,6 +1,12 @@
- #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
- #669 id filtering for charts and reports
- #660 Fix man page section numbers and reference formatting
------ current release ---------------------------
1.8.0 (2025-04-20) -
1.8.0 (2025-04-20) - 2257084710247189231118bc9257180a815ef21a
- #658 Add sub-command 'range' to command 'modify'
(thanks to Sebastian Carlos)

View file

@ -31,7 +31,7 @@ Please visit [timewarrior.net](https://timewarrior.net/docs/) for extensive docu
### From Package
Thanks to the community, there are binary packages available [here](https://timewarrior.net/docs/install.html#distributions).
Thanks to the community, there are binary packages available [here](https://timewarrior.net/docs/install/#distributions).
### Building Timewarrior
@ -93,7 +93,7 @@ For other support options, take a look at [timewarrior.net/support](https://time
Contributions are greatly appreciated.
Whether in the form of code patches, ideas, discussion, bug reports, encouragement or criticism, we need you!
For support options, take a look at [CONTRIBUTING.md](CONTRIBUTING.md) or visit [timewarior.net](https://timewarrior.net/support).
For support options, take a look at [CONTRIBUTING.md](CONTRIBUTING.md) or visit [timewarrior.net](https://timewarrior.net/support).
Visit [GitHub](https://github.com/GothenburgBitFactory/timewarrior) and participate in the future of Timewarrior.

View file

@ -103,14 +103,13 @@ This can be used to see the exclusions.
The ':ids' hint causes the intervals to be displayed with their ids
== EXAMPLES
Charts accept date ranges and tags for filtering, or shortcut hints:
Charts accept date ranges and/or tags, or ids for filtering:
$ timew month 1st - today
$ timew week FOO BAR
$ timew day :week
$ timew day @3 @4
See **timew-ranges**(7) and **timew-hints**(7) on the different ways to provide date ranges.
== SEE ALSO
**timew-day**(1),
**timew-month**(1),
**timew-summary**(1),
**timew-week**(1)

View file

@ -33,5 +33,5 @@ If no arguments are provided, all configuration settings are shown:
...
== SEE ALSO
**timew-hints**(1),
**timew-hints**(7),
**timew-show**(1)

View file

@ -23,4 +23,4 @@ Then having selected '@2' as the interval you wish to delete:
== SEE ALSO
*timew-cancel*(1)
**timew-cancel**(1)

View file

@ -10,7 +10,7 @@ timew-export - export tracked time in JSON
== DESCRIPTION
Exports all the tracked time in JSON format.
Supply either a list of interval IDs (e.g. `@1 @2`), or optional filters (see **timew-ranges(7)** and/or **timew-tags(1)**)
Supply either a list of interval IDs (e.g. `@1 @2`), or optional filters (see **timew-ranges**(7) and/or **timew-tags**(1))
== EXAMPLES

View file

@ -11,4 +11,4 @@ timew-extensions - list available extensions
Displays the directory containing the extension programs and a table showing each extension and its status.
== SEE ALSO
**timew-diagnostics**(1)
**timew-diagnostics**(1)

View file

@ -27,4 +27,4 @@ Note that you can fill multiple intervals:
== SEE ALSO
**timew-hints**(1)
**timew-hints**(7)

View file

@ -19,4 +19,4 @@ For example:
It is an error to reference an interval or tag that does not exist.
== SEE ALSO
*timew-DOM*
**timew-dom**(7)

View file

@ -15,7 +15,7 @@ Using the 'start' or 'end' subcommand, one can either specify a new start or end
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.
One can add the ':adjust' hint to force an overwrite in this case.
See **timew-summary**(1) on how to retrieve the interval id.
@ -46,6 +46,6 @@ As in the examples above, the date portion can be omitted, if the date of the in
== SEE ALSO
**timew-lengthen**(1),
**timew-move**(1),
**timew-resize**(1)
**timew-resize**(1),
**timew-shorten**(1),
**timew-summary**(1)

View file

@ -6,6 +6,7 @@ timew-report - run an extension report
== SYNOPSIS
[verse]
*timew* [*report*] _<report>_ [_<range>_] [_<tag>_**...**]
*timew* [*report*] _<report>_ _<id>_**...**
== DESCRIPTION
Runs an extension report, and supports filtering data.
@ -19,14 +20,17 @@ This does however assume there is a 'foo' extension installed.
The return code is the return code of the extension.
If the extension produces no output and a non-zero rc, then 255 is returned.
Filtering is either possible by range and/or tags, or by ids.
== CONFIGURATION
**reports.range**::
Sets the default date range for all reports.
The value has to correspond to a range hint, see timew-hints(7).
The value has to correspond to a range hint, see **timew-hints**(7).
Defaults to `all`
**reports.**__<name>__**.range**::
Set the date range for report _name_, used if no _range_ is given on the command line.
Here, _name_ is the name of the report executable without its extension (i.e. a report executable 'foo.py' is referred to by 'foo').
The value has to correspond to a range hint, see timew-hints(7).
The value has to correspond to a range hint, see **timew-hints**(7).
Defaults to the value of **reports.range**.

View file

@ -67,7 +67,7 @@ Default value is 'yes'.
**reports.summary.range**::
Set the date range for the summary report.
The value has to correspond to a range hint, see timew-hints(7).
The value has to correspond to a range hint, see **timew-hints**(7).
Default value is 'day'
**reports.summary.weekdays**::

View file

@ -47,14 +47,6 @@ services:
security_opt:
- label=type:container_runtime_t
tty: true
test-fedora40:
build:
context: .
dockerfile: test/docker/fedora40
network_mode: "host"
security_opt:
- label=type:container_runtime_t
tty: true
test-fedora41:
build:
context: .
@ -63,6 +55,14 @@ services:
security_opt:
- label=type:container_runtime_t
tty: true
test-fedora42:
build:
context: .
dockerfile: test/docker/fedora42
network_mode: "host"
security_opt:
- label=type:container_runtime_t
tty: true
test-opensuseleap:
build:
context: .
@ -79,14 +79,6 @@ services:
security_opt:
- label=type:container_runtime_t
tty: true
test-ubuntu2004:
build:
context: .
dockerfile: test/docker/ubuntu2004
network_mode: "host"
security_opt:
- label=type:container_runtime_t
tty: true
test-ubuntu2204:
build:
context: .

View file

@ -460,6 +460,12 @@ void CLI::canonicalizeNames ()
for (auto& a : _args)
{
// Do not canonicalize the BINARY
if (a.hasTag ("BINARY"))
{
continue;
}
auto raw = a.attribute ("raw");
std::string canonical = raw;

View file

@ -28,6 +28,7 @@
#include <ChartConfig.h>
#include <Duration.h>
#include <IntervalFilterAllInRange.h>
#include <IntervalFilterAllWithIds.h>
#include <IntervalFilterAllWithTags.h>
#include <IntervalFilterAndGroup.h>
#include <Range.h>
@ -82,16 +83,57 @@ int renderChart (
Range default_range = {};
expandIntervalHint (":" + report_hint, default_range);
auto range = cli.getRange (default_range);
auto ids = cli.getIds ();
auto tags = cli.getTags ();
// Load the data.
IntervalFilterAndGroup filtering ({
std::make_shared <IntervalFilterAllInRange> (range),
std::make_shared <IntervalFilterAllWithTags> (tags)
});
if (! ids.empty () && ! tags.empty ())
{
throw std::string ("You cannot filter intervals by both, ids and tags.");
}
auto tracked = getTracked (database, rules, filtering);
Range range;
std::vector <Interval> tracked;
if (! ids.empty ())
{
auto filtering = IntervalFilterAllWithIds (ids);
tracked = getTracked (database, rules, filtering);
if (tracked.size () != ids.size ())
{
for (auto& id: ids)
{
bool found = false;
for (auto& interval: tracked)
{
if (interval.id == id)
{
found = true;
break;
}
}
if (! found)
{
throw format ("ID '@{1}' does not correspond to any tracking.", id);
}
}
}
range = cli.getRange (default_range);
}
else
{
range = cli.getRange (default_range);
IntervalFilterAndGroup filtering ({
std::make_shared <IntervalFilterAllInRange> (range),
std::make_shared <IntervalFilterAllWithTags> (tags)
});
tracked = getTracked (database, rules, filtering);
}
if (tracked.empty ())
{

View file

@ -29,6 +29,7 @@
#include <IntervalFilterAllWithTags.h>
#include <IntervalFilterAndGroup.h>
#include <commands.h>
#include <format.h>
#include <iostream>
#include <timew.h>
@ -39,33 +40,52 @@ int CmdExport (
Database& database)
{
auto ids = cli.getIds ();
auto range = cli.getRange ();
auto tags = cli.getTags ();
std::shared_ptr <IntervalFilter> filtering;
if (! ids.empty () && ! tags.empty ())
{
throw std::string ("You cannot specify both id and tags/range to export intervals.");
}
std::vector <Interval> intervals;
if (! ids.empty ())
{
if (! range.is_empty ())
{
throw std::string ("You cannot specify both id and tags/range to export intervals.");
}
auto filtering = IntervalFilterAllWithIds (ids);
intervals = getTracked (database, rules, filtering);
filtering = std::make_shared <IntervalFilterAllWithIds> (ids);
if (intervals.size () != ids.size ())
{
for (auto& id: ids)
{
bool found = false;
for (auto& interval: intervals)
{
if (interval.id == id)
{
found = true;
break;
}
}
if (! found)
{
throw format ("ID '@{1}' does not correspond to any tracking.", id);
}
}
}
}
else
{
filtering = std::make_shared <IntervalFilterAndGroup> (
std::vector <std::shared_ptr <IntervalFilter>> (
{
std::make_shared <IntervalFilterAllInRange> (range),
std::make_shared <IntervalFilterAllWithTags> (tags),
}
)
);
}
auto range = cli.getRange ();
auto intervals = getTracked (database, rules, *filtering);
IntervalFilterAndGroup filtering ({
std::make_shared <IntervalFilterAllInRange> (range),
std::make_shared <IntervalFilterAllWithTags> (tags)
});
intervals = getTracked (database, rules, filtering);
}
std::cout << jsonFromIntervals (intervals);

View file

@ -26,6 +26,7 @@
#include <FS.h>
#include <IntervalFilterAllInRange.h>
#include <IntervalFilterAllWithIds.h>
#include <IntervalFilterAllWithTags.h>
#include <IntervalFilterAndGroup.h>
#include <cmake.h>
@ -133,16 +134,57 @@ int CmdReport (
Range default_range = {};
expandIntervalHint (":" + report_hint, default_range);
// Create a filter, and if empty, choose the current week.
auto ids = cli.getIds ();
auto tags = cli.getTags ();
auto range = cli.getRange (default_range);
IntervalFilterAndGroup filtering ({
std::make_shared <IntervalFilterAllInRange> (range),
std::make_shared <IntervalFilterAllWithTags> (tags)
});
if (! ids.empty () && ! tags.empty ())
{
throw std::string ("You cannot filter intervals by both, ids and tags.");
}
auto tracked = getTracked (database, rules, filtering);
Range range;
std::vector <Interval> tracked;
if (! ids.empty ())
{
auto filtering = IntervalFilterAllWithIds (ids);
tracked = getTracked (database, rules, filtering);
if (tracked.size () != ids.size ())
{
for (auto& id: ids)
{
bool found = false;
for (auto& interval: tracked)
{
if (interval.id == id)
{
found = true;
break;
}
}
if (! found)
{
throw format ("ID '@{1}' does not correspond to any tracking.", id);
}
}
}
range = Range {tracked.begin ()->end, tracked.end ()->start};
}
else
{
range = cli.getRange (default_range);
IntervalFilterAndGroup filtering ({
std::make_shared <IntervalFilterAllInRange> (range),
std::make_shared <IntervalFilterAllWithTags> (tags)
});
tracked = getTracked (database, rules, filtering);
}
// Compose Header info.
rules.set ("temp.report.start", range.is_started () ? range.start.toISO () : "");

@ -1 +1 @@
Subproject commit 2aa844cb9b015fca81b947c57fde07999ede002b
Subproject commit 121f757c3ec1b1f548f7835208b8c72d85d141a7

View file

@ -148,7 +148,13 @@ void initializeDirs (const CLI& cli, Rules& rules)
if (! configFileLocation.exists ())
{
File (configFileLocation).create (0600);
File configFile (configFileLocation);
configFile.create (0600);
std::vector<std::string> defaultConfig = {
"reports.summary.ids = yes\n",
"reports.summary.annotations = yes\n",
};
configFile.append(defaultConfig);
}
// Load the configuration data.

View file

@ -1,4 +1,4 @@
FROM fedora:40
FROM fedora:42
RUN dnf update -y
RUN dnf install -y \

View file

@ -1,42 +0,0 @@
FROM ubuntu:20.04
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && yes | unminimize
RUN apt-get install -y \
asciidoctor \
cmake \
g++ \
git \
locales \
man \
man-db \
python3 \
python3-dateutil \
tzdata
# Setup environment
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 10
RUN ln -fs /usr/share/zoneinfo/Europe/Berlin /etc/localtime
RUN dpkg-reconfigure -f noninteractive tzdata
ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.UTF-8
RUN locale-gen "en_US.UTF-8"
# Setup timewarrior
ADD . /root/code/
WORKDIR /root/code/
RUN git clean -dfx
RUN git submodule init
RUN git submodule update
RUN cmake -DCMAKE_BUILD_TYPE=debug .
RUN make -j2
RUN make install
# Setup tests
WORKDIR /root/code/test/
RUN make -j2
CMD ["bash", "-c", "./run_all -v ; cat all.log | grep 'not ok' ; ./problems ; FAILED=$? ; echo timew $( timew --version ) ; python --version ; cmake --version ; gcc --version ; asciidoctor --version ; exit $FAILED"]

View file

@ -58,10 +58,10 @@ class TestSummary(TestCase):
code, out, err = self.t("summary :ids {:%Y-%m-%d} - {:%Y-%m-%d}".format(yesterday, tomorrow))
self.assertRegex(out, r"""
Wk ?Date Day ID Tags ?Start ?End Time Total
Wk ?Date Day ID Tags Annotation ?Start ?End Time Total
[ -]+
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2}(
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2})?
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2}(
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2})?
[ ]+1:00:0[01]
""")
@ -77,10 +77,10 @@ W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2
code, out, err = self.t("summary :ids :all".format(yesterday, tomorrow))
self.assertRegex(out, r"""
Wk ?Date Day ID Tags ?Start ?End Time Total
Wk ?Date Day ID Tags Annotation ?Start ?End Time Total
[ -]+
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2}(
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2})?
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2}(
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2})?
[ ]+1:00:0[01]
""")
@ -96,10 +96,10 @@ W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2
code, out, err = self.t("summary :ids {:%Y-%m-%d} - {:%Y-%m-%d}".format(yesterday, tomorrow))
self.assertRegex(out, r"""
Wk ?Date Day ID Tags ?Start End Time Total
Wk ?Date Day ID Tags Annotation ?Start End Time Total
[ -]+
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2}[ ]+- \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2}(
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2}[ ]+- \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2})?
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2}[ ]+- \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2}(
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2}[ ]+- \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2})?
[ ]+1:00:0[01]
""")
@ -115,10 +115,10 @@ W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2}[ ]+- \d{1,2}:\d{2}
code, out, err = self.t("summary :ids :all".format(yesterday, tomorrow))
self.assertRegex(out, r"""
Wk ?Date Day ID Tags ?Start End Time Total
Wk ?Date Day ID Tags Annotation ?Start End Time Total
[ -]+
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2}[ ]+- \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2}(
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2}[ ]+- \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2})?
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2}[ ]+- \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2}(
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2}[ ]+- \d{1,2}:\d{2}:\d{2} \d{1,2}:\d{2}:\d{2})?
[ ]+1:00:0[01]
""")
@ -133,12 +133,12 @@ W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 ?\d{1,2}:\d{2}:\d{2}[ ]+- \d{1,2}:\d{2}
code, out, err = self.t("summary 2017-03-09T11:00 - 2017-03-09T12:00 :ids")
self.assertIn("""
Wk Date Day ID Tags Start End Time Total
--- ---------- --- -- ---------- -------- -------- ------- -------
W10 2017-03-09 Thu @3 Tag2 11:38:39 11:45:35 0:06:56
@2 Tag2, Tag3 11:46:21 12:00:17 0:13:56 0:20:52
Wk Date Day ID Tags Annotation Start End Time Total
--- ---------- --- -- ---------- ---------- -------- -------- ------- -------
W10 2017-03-09 Thu @3 Tag2 11:38:39 11:45:35 0:06:56
@2 Tag2, Tag3 11:46:21 12:00:17 0:13:56 0:20:52
0:20:52
0:20:52
""", out)
def test_with_date_filter(self):
@ -150,11 +150,11 @@ W10 2017-03-09 Thu @3 Tag2 11:38:39 11:45:35 0:06:56
code, out, err = self.t("summary 2017-03-10 :ids")
self.assertIn("""
Wk Date Day ID Tags Start End Time Total
--- ---------- --- -- ---- -------- -------- ------- -------
W10 2017-03-10 Fri @2 10:00:00 11:00:00 1:00:00 1:00:00
Wk Date Day ID Tags Annotation Start End Time Total
--- ---------- --- -- ---- ---------- -------- -------- ------- -------
W10 2017-03-10 Fri @2 10:00:00 11:00:00 1:00:00 1:00:00
1:00:00
1:00:00
""", out)
def test_with_tag_filter(self):
@ -167,11 +167,11 @@ W10 2017-03-10 Fri @2 10:00:00 11:00:00 1:00:00 1:00:00
code, out, err = self.t("summary 2017-03-09 Tag1 :ids")
self.assertIn("""
Wk Date Day ID Tags Start End Time Total
--- ---------- --- -- ---- ------- ------- ------- -------
W10 2017-03-09 Thu @4 Tag1 8:43:08 9:38:15 0:55:07 0:55:07
Wk Date Day ID Tags Annotation Start End Time Total
--- ---------- --- -- ---- ---------- ------- ------- ------- -------
W10 2017-03-09 Thu @4 Tag1 8:43:08 9:38:15 0:55:07 0:55:07
0:55:07
0:55:07
""", out)
def test_with_id_filter(self):
@ -184,12 +184,12 @@ W10 2017-03-09 Thu @4 Tag1 8:43:08 9:38:15 0:55:07 0:55:07
code, out, err = self.t("summary @2 @4 :ids")
self.assertIn("""
Wk Date Day ID Tags Start End Time Total
--- ---------- --- -- ---------- -------- -------- ------- -------
W10 2017-03-09 Thu @4 Tag1 8:43:08 9:38:15 0:55:07
@2 Tag2, Tag3 11:46:21 12:00:17 0:13:56 1:09:03
Wk Date Day ID Tags Annotation Start End Time Total
--- ---------- --- -- ---------- ---------- -------- -------- ------- -------
W10 2017-03-09 Thu @4 Tag1 8:43:08 9:38:15 0:55:07
@2 Tag2, Tag3 11:46:21 12:00:17 0:13:56 1:09:03
1:09:03
1:09:03
""", out)
def test_non_contiguous_with_tag_filter(self):
@ -202,12 +202,12 @@ W10 2017-03-09 Thu @4 Tag1 8:43:08 9:38:15 0:55:07
code, out, err = self.t("summary 2017-03-09 Tag1 :ids")
self.assertIn("""
Wk Date Day ID Tags Start End Time Total
--- ---------- --- -- ---------- -------- -------- ------- -------
W10 2017-03-09 Thu @4 Tag1 8:43:08 9:38:15 0:55:07
@2 Tag1, Tag3 11:46:21 12:00:17 0:13:56 1:09:03
Wk Date Day ID Tags Annotation Start End Time Total
--- ---------- --- -- ---------- ---------- -------- -------- ------- -------
W10 2017-03-09 Thu @4 Tag1 8:43:08 9:38:15 0:55:07
@2 Tag1, Tag3 11:46:21 12:00:17 0:13:56 1:09:03
1:09:03
1:09:03
""", out)
def test_with_all_hint(self):
@ -229,13 +229,13 @@ W10 2017-03-09 Thu @4 Tag1 8:43:08 9:38:15 0:55:07
week_col_padding = 2 if (week_yesterday > 9 or week_now > 9 or week_tomorrow > 9) else 1
self.assertIn("""
Wk{7: <{width}}Date Day ID Tags Start End Time Total
-{6:-<{width}} ---------- --- -- ---- -------- -------- ------- -------
W{3: <{width}} {0:%Y-%m-%d} {0:%a} @3 FOO 10:00:00 11:00:00 1:00:00 1:00:00
W{4: <{width}} {1:%Y-%m-%d} {1:%a} @2 BAR 10:00:00 11:00:00 1:00:00 1:00:00
W{5: <{width}} {2:%Y-%m-%d} {2:%a} @1 BAZ 10:00:00 11:00:00 1:00:00 1:00:00
Wk{7: <{width}}Date Day ID Tags Annotation Start End Time Total
-{6:-<{width}} ---------- --- -- ---- ---------- -------- -------- ------- -------
W{3: <{width}} {0:%Y-%m-%d} {0:%a} @3 FOO 10:00:00 11:00:00 1:00:00 1:00:00
W{4: <{width}} {1:%Y-%m-%d} {1:%a} @2 BAR 10:00:00 11:00:00 1:00:00 1:00:00
W{5: <{width}} {2:%Y-%m-%d} {2:%a} @1 BAZ 10:00:00 11:00:00 1:00:00 1:00:00
{7: <{width}} 3:00:00
{7: <{width}} 3:00:00
""".format(yesterday, now, tomorrow,
week_yesterday, week_now, week_tomorrow,
"-", " ", width=week_col_padding), out)
@ -271,11 +271,11 @@ W{5: <{width}} {2:%Y-%m-%d} {2:%a} @1 BAZ 10:00:00 11:00:00 1:00:00 1:00:00
two_digit_week = (week_yesterday > 9)
self.assertIn("""
Wk{2} Date Day ID Tags Start End Time Total
--{3} ---------- --- -- ---- -------- -------- ------- -------
W{1} {0:%Y-%m-%d} {0:%a} @3 FOO 10:00:00 11:00:00 1:00:00 1:00:00
Wk{2} Date Day ID Tags Annotation Start End Time Total
--{3} ---------- --- -- ---- ---------- -------- -------- ------- -------
W{1} {0:%Y-%m-%d} {0:%a} @3 FOO 10:00:00 11:00:00 1:00:00 1:00:00
{2} 1:00:00
{2} 1:00:00
""".format(yesterday, week_yesterday, " " if two_digit_week is True else "", "-" if two_digit_week is True else ""), out)
def test_with_named_date_today(self):
@ -295,11 +295,11 @@ W{1} {0:%Y-%m-%d} {0:%a} @3 FOO 10:00:00 11:00:00 1:00:00 1:00:00
two_digit_week = (week_now > 9)
self.assertIn("""
Wk{2} Date Day ID Tags Start End Time Total
--{3} ---------- --- -- ---- -------- -------- ------- -------
W{1} {0:%Y-%m-%d} {0:%a} @2 BAR 10:00:00 11:00:00 1:00:00 1:00:00
Wk{2} Date Day ID Tags Annotation Start End Time Total
--{3} ---------- --- -- ---- ---------- -------- -------- ------- -------
W{1} {0:%Y-%m-%d} {0:%a} @2 BAR 10:00:00 11:00:00 1:00:00 1:00:00
{2} 1:00:00
{2} 1:00:00
""".format(now, week_now, " " if two_digit_week is True else "", "-" if two_digit_week is True else ""), out)
def test_with_day_gap(self):
@ -310,12 +310,12 @@ W{1} {0:%Y-%m-%d} {0:%a} @2 BAR 10:00:00 11:00:00 1:00:00 1:00:00
code, out, err = self.t("summary 2017-03-09 - 2017-03-12 :ids")
self.assertIn("""
Wk Date Day ID Tags Start End Time Total
--- ---------- --- -- ---- -------- -------- ------- -------
W10 2017-03-09 Thu @2 10:00:00 11:00:00 1:00:00 1:00:00
W10 2017-03-11 Sat @1 10:00:00 11:00:00 1:00:00 1:00:00
Wk Date Day ID Tags Annotation Start End Time Total
--- ---------- --- -- ---- ---------- -------- -------- ------- -------
W10 2017-03-09 Thu @2 10:00:00 11:00:00 1:00:00 1:00:00
W10 2017-03-11 Sat @1 10:00:00 11:00:00 1:00:00 1:00:00
2:00:00
2:00:00
""", out)
def test_with_week_change(self):
@ -326,12 +326,12 @@ W10 2017-03-11 Sat @1 10:00:00 11:00:00 1:00:00 1:00:00
code, out, err = self.t("summary 2017-03-11 - 2017-03-14 :ids")
self.assertIn("""
Wk Date Day ID Tags Start End Time Total
--- ---------- --- -- ---- -------- -------- ------- -------
W10 2017-03-11 Sat @2 10:00:00 11:00:00 1:00:00 1:00:00
W11 2017-03-13 Mon @1 10:00:00 11:00:00 1:00:00 1:00:00
Wk Date Day ID Tags Annotation Start End Time Total
--- ---------- --- -- ---- ---------- -------- -------- ------- -------
W10 2017-03-11 Sat @2 10:00:00 11:00:00 1:00:00 1:00:00
W11 2017-03-13 Mon @1 10:00:00 11:00:00 1:00:00 1:00:00
2:00:00
2:00:00
""", out)
def test_with_several_tracks_per_day(self):
@ -346,16 +346,16 @@ W11 2017-03-13 Mon @1 10:00:00 11:00:00 1:00:00 1:00:00
code, out, err = self.t("summary 2017-03-09 - 2017-03-12 :ids")
self.assertIn("""
Wk Date Day ID Tags Start End Time Total
--- ---------- --- -- ---- -------- -------- ------- -------
W10 2017-03-09 Thu @6 FOO 10:00:00 11:00:00 1:00:00
@5 BAR 12:00:00 13:00:00 1:00:00
@4 BAZ 14:00:00 15:00:00 1:00:00 3:00:00
W10 2017-03-11 Sat @3 FOO 10:00:00 11:00:00 1:00:00
@2 BAR 12:00:00 13:00:00 1:00:00
@1 BAZ 14:00:00 15:00:00 1:00:00 3:00:00
Wk Date Day ID Tags Annotation Start End Time Total
--- ---------- --- -- ---- ---------- -------- -------- ------- -------
W10 2017-03-09 Thu @6 FOO 10:00:00 11:00:00 1:00:00
@5 BAR 12:00:00 13:00:00 1:00:00
@4 BAZ 14:00:00 15:00:00 1:00:00 3:00:00
W10 2017-03-11 Sat @3 FOO 10:00:00 11:00:00 1:00:00
@2 BAR 12:00:00 13:00:00 1:00:00
@1 BAZ 14:00:00 15:00:00 1:00:00 3:00:00
6:00:00
6:00:00
""", out)
def test_with_empty_interval_at_start_of_day(self):
@ -363,9 +363,9 @@ W10 2017-03-11 Sat @3 FOO 10:00:00 11:00:00 1:00:00
self.t("track sod - sod")
code, out, err = self.t("summary :year")
self.assertRegex(out, r"""
Wk ?Date Day Tags ?Start ?End Time Total
Wk ?Date Day ID Tags Annotation Start End Time Total
[ -]+
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} ?0:00:00 0:00:00 0:00:00 0:00:00
W\d{1,2} \d{4}-\d{2}-\d{2} .{3} @1 0:00:00 0:00:00 0:00:00 0:00:00
[ ]+0:00:00
""")

View file

@ -118,6 +118,9 @@ class TestUndo(TestCase):
def test_undo_config_add_name(self):
"""Test undo of command 'config' (add name)"""
self.t("config")
initial = [x for x in self.t.timewrc_content if x != '\n']
self.t("config foo bar :yes")
before = [x for x in self.t.timewrc_content if x != '\n']
@ -127,7 +130,7 @@ class TestUndo(TestCase):
after = [x for x in self.t.timewrc_content if x != '\n']
self.assertNotEqual(before, after)
self.assertEqual([], after)
self.assertEqual(initial, after)
def test_undo_config_remove_name(self):
"""Test undo of command 'config' (remove name)"""