Replace CLI::getFilter by CLI::getRange and CLI::getTags

- Add CLI::getRange
- Make CLI::getTags return a set instead of a vector. This has the side effect that the tags are sorted now...
- Replace variable 'filter' by 'range' and 'tags'. Variable 'filter' was just a wrapper, better use the components directly
This commit is contained in:
Thomas Lauf 2023-04-14 19:10:14 +02:00
parent 0c84dfd42e
commit 34bba44d38
28 changed files with 221 additions and 219 deletions

View file

@ -564,14 +564,14 @@ std::set<int> CLI::getIds() const
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::vector<std::string> CLI::getTags () const std::set <std::string> CLI::getTags () const
{ {
std::vector <std::string> tags; std::set <std::string> tags;
for (auto& arg : _args) for (auto& arg : _args)
{ {
if (arg.hasTag ("TAG")) if (arg.hasTag ("TAG"))
tags.push_back (arg.attribute ("raw")); tags.insert (arg.attribute ("raw"));
} }
return tags; return tags;
@ -628,20 +628,18 @@ std::vector <std::string> CLI::getDomReferences () const
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// A filter is just another interval, containing start, end and tags.
// //
// Supported interval forms: // Supported range forms:
// ["from"] <date> ["to"|"-" <date>] // ["from"] <date> ["to"|"-" <date>]
// ["from"] <date> "for" <duration> // ["from"] <date> "for" <duration>
// <duration> ["before"|"after" <date>] // <duration> ["before"|"after" <date>]
// <duration> "ago" // <duration> "ago"
// //
Interval CLI::getFilter (const Range& default_range) const Range CLI::getRange(const Range& default_range) const
{ {
// One instance, so we can directly compare.
Datetime now; Datetime now;
Interval filter; Range the_range;
std::string start; std::string start;
std::string end; std::string end;
std::string duration; std::string duration;
@ -666,16 +664,16 @@ Interval CLI::getFilter (const Range& default_range) const
{ {
if (range.is_empty ()) if (range.is_empty ())
{ {
args.emplace_back ("<all>"); args.emplace_back("<all>");
} }
else else
{ {
start = range.start.toISO (); start = range.start.toISO ();
end = range.end.toISO (); end = range.end.toISO ();
args.emplace_back ("<date>"); args.emplace_back("<date>");
args.emplace_back ("-"); args.emplace_back("-");
args.emplace_back ("<date>"); args.emplace_back("<date>");
} }
} }
@ -688,14 +686,14 @@ Interval CLI::getFilter (const Range& default_range) const
else if (end.empty ()) else if (end.empty ())
end = raw; end = raw;
args.emplace_back ("<date>"); args.emplace_back("<date>");
} }
else if (arg._lextype == Lexer::Type::duration) else if (arg._lextype == Lexer::Type::duration)
{ {
if (duration.empty ()) if (duration.empty ())
duration = raw; duration = raw;
args.emplace_back ("<duration>"); args.emplace_back("<duration>");
} }
else if (arg.hasTag ("KEYWORD")) else if (arg.hasTag ("KEYWORD"))
{ {
@ -704,37 +702,27 @@ Interval CLI::getFilter (const Range& default_range) const
// function. // function.
args.push_back (raw); args.push_back (raw);
} }
else if (arg.hasTag ("ID"))
{
// Not part of a filter.
}
else
{
filter.tag (raw);
}
} }
} }
if (args.empty ()) if (args.empty ())
{ {
filter.setRange(default_range); the_range = default_range;
} }
// <date> // <date>
else if (args.size () == 1 && else if (args.size () == 1 &&
args[0] == "<date>") args[0] == "<date>")
{ {
DatetimeParser dtp; DatetimeParser dtp;
Range range = dtp.parse_range(start); Range range = dtp.parse_range(start);
filter.setRange (range); the_range = Range (range);
} }
// from <date> // from <date>
else if (args.size () == 2 && else if (args.size () == 2 &&
args[0] == "from" && args[0] == "from" &&
args[1] == "<date>") args[1] == "<date>")
{ {
filter.setRange ({Datetime (start), 0}); the_range = Range ({Datetime (start), 0});
} }
// <date> to/- <date> // <date> to/- <date>
else if (args.size () == 3 && else if (args.size () == 3 &&
@ -742,9 +730,8 @@ Interval CLI::getFilter (const Range& default_range) const
(args[1] == "to" || args[1] == "-") && (args[1] == "to" || args[1] == "-") &&
args[2] == "<date>") args[2] == "<date>")
{ {
filter.setRange ({Datetime (start), Datetime (end)}); the_range = Range ({Datetime (start), Datetime (end)});
} }
// from <date> to/- <date> // from <date> to/- <date>
else if (args.size () == 4 && else if (args.size () == 4 &&
args[0] == "from" && args[0] == "from" &&
@ -752,18 +739,16 @@ Interval CLI::getFilter (const Range& default_range) const
(args[2] == "to" || args[2] == "-") && (args[2] == "to" || args[2] == "-") &&
args[3] == "<date>") args[3] == "<date>")
{ {
filter.setRange ({Datetime (start), Datetime (end)}); the_range = Range ({Datetime (start), Datetime (end)});
} }
// <date> for <duration> // <date> for <duration>
else if (args.size () == 3 && else if (args.size () == 3 &&
args[0] == "<date>" && args[0] == "<date>" &&
args[1] == "for" && args[1] == "for" &&
args[2] == "<duration>") args[2] == "<duration>")
{ {
filter.setRange ({Datetime (start), Datetime (start) + Duration (duration).toTime_t ()}); the_range = Range ({Datetime (start), Datetime (start) + Duration (duration).toTime_t ()});
} }
// from <date> for <duration> // from <date> for <duration>
else if (args.size () == 4 && else if (args.size () == 4 &&
args[0] == "from" && args[0] == "from" &&
@ -771,66 +756,61 @@ Interval CLI::getFilter (const Range& default_range) const
args[2] == "for" && args[2] == "for" &&
args[3] == "<duration>") args[3] == "<duration>")
{ {
filter.setRange ({Datetime (start), Datetime (start) + Duration (duration).toTime_t ()}); the_range = Range ({Datetime (start), Datetime (start) + Duration (duration).toTime_t ()});
} }
// <duration> before <date> // <duration> before <date>
else if (args.size () == 3 && else if (args.size () == 3 &&
args[0] == "<duration>" && args[0] == "<duration>" &&
args[1] == "before" && args[1] == "before" &&
args[2] == "<date>") args[2] == "<date>")
{ {
filter.setRange ({Datetime (start) - Duration (duration).toTime_t (), Datetime (start)}); the_range = Range ({Datetime (start) - Duration (duration).toTime_t (), Datetime (start)});
} }
// <duration> after <date> // <duration> after <date>
else if (args.size () == 3 && else if (args.size () == 3 &&
args[0] == "<duration>" && args[0] == "<duration>" &&
args[1] == "after" && args[1] == "after" &&
args[2] == "<date>") args[2] == "<date>")
{ {
filter.setRange ({Datetime (start), Datetime (start) + Duration (duration).toTime_t ()}); the_range = Range ({Datetime (start), Datetime (start) + Duration (duration).toTime_t ()});
} }
// <duration> ago // <duration> ago
else if (args.size () == 2 && else if (args.size () == 2 &&
args[0] == "<duration>" && args[0] == "<duration>" &&
args[1] == "ago") args[1] == "ago")
{ {
filter.setRange ({now - Duration (duration).toTime_t (), 0}); the_range = Range ({now - Duration (duration).toTime_t (), 0});
} }
// for <duration> // for <duration>
else if (args.size () == 2 && else if (args.size () == 2 &&
args[0] == "for" && args[0] == "for" &&
args[1] == "<duration>") args[1] == "<duration>")
{ {
filter.setRange ({now - Duration (duration).toTime_t (), now}); the_range = Range ({now - Duration (duration).toTime_t (), now});
} }
// <duration> // <duration>
else if (args.size () == 1 && else if (args.size () == 1 &&
args[0] == "<duration>") args[0] == "<duration>")
{ {
filter.setRange ({now - Duration (duration).toTime_t (), now}); the_range = Range ({now - Duration (duration).toTime_t (), now});
} }
// :all // :all
else if (args.size () == 1 && args[0] == "<all>") else if (args.size () == 1 && args[0] == "<all>")
{ {
filter.setRange (0, 0); the_range = Range (0, 0);
} }
// Unrecognized date range construct.
// Unrecognized date range construct.
else if (! args.empty ()) else if (! args.empty ())
{ {
throw std::string ("Unrecognized date range: '") + join (" ", args) + "'."; throw std::string ("Unrecognized date range: '") + join (" ", args) + "'.";
} }
if (filter.end != 0 && filter.start > filter.end) if (the_range.end != 0 && the_range.start > the_range.end)
{
throw std::string ("The end of a date range must be after the start."); throw std::string ("The end of a date range must be after the start.");
}
return filter; return the_range;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -70,11 +70,11 @@ public:
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::set <int> getIds () const; std::set <int> getIds () const;
std::vector<std::string> getTags () const; std::set<std::string> getTags () const;
std::string getAnnotation() const; std::string getAnnotation() const;
Duration getDuration() const; Duration getDuration() const;
std::vector<std::string> getDomReferences () const; std::vector<std::string> getDomReferences () const;
Interval getFilter (const Range& = {}) const; Range getRange (const Range& default_range = {0, 0}) const;
std::string dump (const std::string& title = "CLI Parser") const; std::string dump (const std::string& title = "CLI Parser") const;
private: private:

View file

@ -61,14 +61,14 @@ Chart::Chart (const ChartConfig& configuration) :
{ } { }
std::string Chart::render ( std::string Chart::render (
const Interval &filter, const Range& range,
const std::vector<Interval> &tracked, const std::vector<Interval> &tracked,
const std::vector<Range> &exclusions, const std::vector<Range> &exclusions,
const std::map<Datetime, std::string> &holidays) const std::map<Datetime, std::string> &holidays)
{ {
// Determine hours shown. // Determine hours shown.
auto hour_range = determine_hour_range auto hour_range = determine_hour_range
? determineHourRange (filter, tracked) ? determineHourRange (range, tracked)
: std::make_pair (0, 23); : std::make_pair (0, 23);
int first_hour = hour_range.first; int first_hour = hour_range.first;
@ -97,7 +97,7 @@ std::string Chart::render (
// Each day is rendered separately. // Each day is rendered separately.
time_t total_work = 0; time_t total_work = 0;
for (Datetime day = filter.start; day < filter.end; day++) for (Datetime day = range.start; day < range.end; day++)
{ {
// Render the exclusion blocks. // Render the exclusion blocks.
@ -154,7 +154,7 @@ std::string Chart::render (
out << (with_totals ? renderSubTotal (total_work, std::string (padding_size, ' ')) : "") out << (with_totals ? renderSubTotal (total_work, std::string (padding_size, ' ')) : "")
<< (with_holidays ? renderHolidays (holidays) : "") << (with_holidays ? renderHolidays (holidays) : "")
<< (with_summary ? renderSummary (indent, filter, exclusions, tracked) : ""); << (with_summary ? renderSummary (indent, range, exclusions, tracked) : "");
return out.str (); return out.str ();
} }
@ -172,7 +172,7 @@ unsigned long Chart::getIndentSize ()
// Scan all tracked intervals, looking for the earliest and latest hour into // Scan all tracked intervals, looking for the earliest and latest hour into
// which an interval extends. // which an interval extends.
std::pair<int, int> Chart::determineHourRange ( std::pair<int, int> Chart::determineHourRange (
const Interval &filter, const Range& range,
const std::vector<Interval> &tracked) const std::vector<Interval> &tracked)
{ {
// If there is no data, show the whole day. // If there is no data, show the whole day.
@ -185,11 +185,11 @@ std::pair<int, int> Chart::determineHourRange (
auto first_hour = 23; auto first_hour = 23;
auto last_hour = 0; auto last_hour = 0;
for (Datetime day = filter.start; day < filter.end; day++) for (Datetime day = range.start; day < range.end; day++)
{ {
auto day_range = getFullDay (day); auto day_range = getFullDay (day);
for (auto &track : tracked) for (auto& track : tracked)
{ {
Interval test {track}; Interval test {track};
@ -549,7 +549,7 @@ std::string Chart::renderHolidays (const std::map<Datetime, std::string> &holida
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string Chart::renderSummary ( std::string Chart::renderSummary (
const std::string &indent, const std::string &indent,
const Interval &filter, const Range& range,
const std::vector<Range> &exclusions, const std::vector<Range> &exclusions,
const std::vector<Interval> &tracked) const std::vector<Interval> &tracked)
{ {
@ -558,9 +558,9 @@ std::string Chart::renderSummary (
for (auto &exclusion : exclusions) for (auto &exclusion : exclusions)
{ {
if (filter.overlaps (exclusion)) if (range.overlaps (exclusion))
{ {
total_unavailable += filter.intersect (exclusion).total (); total_unavailable += range.intersect (exclusion).total ();
} }
} }
@ -570,9 +570,9 @@ std::string Chart::renderSummary (
{ {
for (auto &interval : tracked) for (auto &interval : tracked)
{ {
if (filter.overlaps (interval)) if (range.overlaps (interval))
{ {
Interval clipped = clip (interval, filter); Interval clipped = clip (interval, range);
if (interval.is_open ()) if (interval.is_open ())
{ {
clipped.end = reference_datetime; clipped.end = reference_datetime;
@ -583,7 +583,7 @@ std::string Chart::renderSummary (
} }
} }
auto total_available = filter.total () - total_unavailable; auto total_available = range.total () - total_unavailable;
assert (total_available >= 0); assert (total_available >= 0);
auto total_remaining = total_available - total_worked; auto total_remaining = total_available - total_worked;

View file

@ -37,7 +37,7 @@ class Chart
public: public:
explicit Chart (const ChartConfig& configuration); explicit Chart (const ChartConfig& configuration);
std::string render (const Interval&, const std::vector <Interval>&, const std::vector <Range>&, const std::map <Datetime, std::string>&); std::string render (const Range&, const std::vector <Interval>&, const std::vector <Range>&, const std::map <Datetime, std::string>&);
private: private:
std::string renderAxis (int, int); std::string renderAxis (int, int);
@ -45,7 +45,7 @@ private:
std::string renderHolidays (const std::map <Datetime, std::string>&); std::string renderHolidays (const std::map <Datetime, std::string>&);
std::string renderMonth (const Datetime&, const Datetime&); std::string renderMonth (const Datetime&, const Datetime&);
std::string renderSubTotal (time_t, const std::string&); std::string renderSubTotal (time_t, const std::string&);
std::string renderSummary (const std::string&, const Interval&, const std::vector <Range>&, const std::vector <Interval>&); std::string renderSummary (const std::string&, const Range&, const std::vector <Range>&, const std::vector <Interval>&);
std::string renderTotal (time_t); std::string renderTotal (time_t);
std::string renderWeek (const Datetime&, const Datetime&); std::string renderWeek (const Datetime&, const Datetime&);
std::string renderWeekday (Datetime&, const Color&); std::string renderWeekday (Datetime&, const Color&);
@ -55,7 +55,7 @@ private:
unsigned long getIndentSize (); unsigned long getIndentSize ();
std::pair <int, int> determineHourRange (const Interval&, const std::vector <Interval>&); std::pair <int, int> determineHourRange (const Range&, const std::vector <Interval>&);
Color getDayColor (const Datetime&, const std::map <Datetime, std::string>&); Color getDayColor (const Datetime&, const std::map <Datetime, std::string>&);
Color getHourColor (int) const; Color getHourColor (int) const;

View file

@ -86,7 +86,7 @@ Range DatetimeParser::parse_range (const std::string& input)
{ {
start = pig.cursor (); start = pig.cursor ();
resolve (); resolve ();
return Range { Datetime {_date}, 0 }; return Range {Datetime {_date}, 0};
} }
} }
@ -115,13 +115,13 @@ Range DatetimeParser::parse_range (const std::string& input)
{ {
auto start_date = Datetime (_date); auto start_date = Datetime (_date);
auto end_date = Datetime(start_date.year(), start_date.month()+1, 1); auto end_date = Datetime(start_date.year(), start_date.month()+1, 1);
return Range { start_date, end_date }; return Range {start_date, end_date};
} }
else if (_year != 0) else if (_year != 0)
{ {
auto start_date = Datetime (_date); auto start_date = Datetime (_date);
auto end_date = Datetime(start_date.year()+1, 1, 1); auto end_date = Datetime(start_date.year()+1, 1, 1);
return Range { start_date, end_date }; return Range {start_date, end_date};
} }
return Range {}; return Range {};
} }
@ -145,7 +145,7 @@ Range DatetimeParser::parse_range (const std::string& input)
{ {
start = pig.cursor (); start = pig.cursor ();
resolve (); resolve ();
return Range { Datetime (_date), 0 }; return Range {Datetime (_date), 0};
} }
} }
@ -153,14 +153,14 @@ Range DatetimeParser::parse_range (const std::string& input)
if (parse_informal_time (pig)) if (parse_informal_time (pig))
{ {
return Range { Datetime {_date}, 0 }; return Range {Datetime {_date}, 0};
} }
if (parse_named_day (pig)) if (parse_named_day (pig))
{ {
// ::validate and ::resolve are not needed in this case. // ::validate and ::resolve are not needed in this case.
start = pig.cursor (); start = pig.cursor ();
return Range { Datetime (_date), Datetime (_date) + Duration ("1d").toTime_t () }; return Range {Datetime (_date), Datetime (_date) + Duration ("1d").toTime_t ()};
} }
if (parse_named_month (pig)) if (parse_named_month (pig))
@ -171,14 +171,14 @@ Range DatetimeParser::parse_range (const std::string& input)
auto month = (begin.month() + 1) % 13 + (begin.month() == 12); auto month = (begin.month() + 1) % 13 + (begin.month() == 12);
auto year = (begin.year() + (begin.month() == 12)); auto year = (begin.year() + (begin.month() == 12));
auto end = Datetime (year, month, 1); auto end = Datetime (year, month, 1);
return Range { begin, end }; return Range {begin, end};
} }
if (parse_named (pig)) if (parse_named (pig))
{ {
// ::validate and ::resolve are not needed in this case. // ::validate and ::resolve are not needed in this case.
start = pig.cursor (); start = pig.cursor ();
return Range { Datetime (_date), 0 }; return Range {Datetime (_date), 0};
} }
throw format ("'{1}' is not a valid range.", input); throw format ("'{1}' is not a valid range.", input);

View file

@ -30,12 +30,15 @@
#include <Range.h> #include <Range.h>
#include <set> #include <set>
#include <string> #include <string>
#include <utility>
class Interval : public Range class Interval : public Range
{ {
public: public:
Interval () = default; Interval () = default;
Interval (const Datetime& start, const Datetime& end) : Range (start, end) {} Interval (const Datetime& start, const Datetime& end) : Range (start, end) {}
Interval (const Range& range, std::set <std::string> tags) : Range (range), _tags(std::move(tags)) {}
bool operator== (const Interval&) const; bool operator== (const Interval&) const;
bool operator!= (const Interval&) const; bool operator!= (const Interval&) const;

View file

@ -41,8 +41,8 @@ int CmdAnnotate (
{ {
const bool verbose = rules.getBoolean ("verbose"); const bool verbose = rules.getBoolean ("verbose");
std::set <int> ids = cli.getIds (); auto ids = cli.getIds ();
std::string annotation = cli.getAnnotation (); auto annotation = cli.getAnnotation ();
journal.startTransaction (); journal.startTransaction ();
flattenDatabase (database, rules); flattenDatabase (database, rules);

View file

@ -36,9 +36,9 @@
#include <iostream> #include <iostream>
#include <timew.h> #include <timew.h>
int renderChart (const CLI&, const std::string&, Interval&, Rules&, Database&); int renderChart (const std::string&, const CLI&, Rules&, Database&);
std::map <Datetime, std::string> createHolidayMap (Rules&, Interval&); std::map <Datetime, std::string> createHolidayMap (Rules&, Range&);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int CmdChartDay ( int CmdChartDay (
@ -46,16 +46,7 @@ int CmdChartDay (
Rules& rules, Rules& rules,
Database& database) Database& database)
{ {
auto default_hint = rules.get ("reports.range", "day"); return renderChart ("day", cli, rules, database);
auto report_hint = rules.get ("reports.day.range", default_hint);
Range default_range = {};
expandIntervalHint (":" + report_hint, default_range);
// Create a filter, and if empty, choose the current day.
auto filter = cli.getFilter (default_range);
return renderChart (cli, "day", filter, rules, database);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -64,16 +55,7 @@ int CmdChartWeek (
Rules& rules, Rules& rules,
Database& database) Database& database)
{ {
auto default_hint = rules.get ("reports.range", "week"); return renderChart ("week", cli, rules, database);
auto report_hint = rules.get ("reports.week.range", default_hint);
Range default_range = {};
expandIntervalHint (":" + report_hint, default_range);
// Create a filter, and if empty, choose the current week.
auto filter = cli.getFilter (default_range);
return renderChart (cli, "week", filter, rules, database);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -82,32 +64,31 @@ int CmdChartMonth (
Rules& rules, Rules& rules,
Database& database) Database& database)
{ {
auto default_hint = rules.get ("reports.range", "month"); return renderChart ("month", cli, rules, database);
auto report_hint = rules.get ("reports.month.range", default_hint);
Range default_range = {};
expandIntervalHint (":" + report_hint, default_range);
// Create a filter, and if empty, choose the current month.
auto filter = cli.getFilter (default_range);
return renderChart (cli, "month", filter, rules, database);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int renderChart ( int renderChart (
const CLI& cli,
const std::string& type, const std::string& type,
Interval& filter, const CLI& cli,
Rules& rules, Rules& rules,
Database& database) Database& database)
{ {
const bool verbose = rules.getBoolean ("verbose"); const bool verbose = rules.getBoolean ("verbose");
auto default_hint = rules.get ("reports.range", type);
auto report_hint = rules.get ("reports." + type + ".range", default_hint);
Range default_range = {};
expandIntervalHint (":" + report_hint, default_range);
auto range = cli.getRange (default_range);
auto tags = cli.getTags ();
// Load the data. // Load the data.
IntervalFilterAndGroup filtering ({ IntervalFilterAndGroup filtering ({
std::make_shared <IntervalFilterAllInRange> ( Range { filter.start, filter.end }), std::make_shared <IntervalFilterAllInRange> (range),
std::make_shared <IntervalFilterAllWithTags> (filter.tags()) std::make_shared <IntervalFilterAllWithTags> (tags)
}); });
auto tracked = getTracked (database, rules, filtering); auto tracked = getTracked (database, rules, filtering);
@ -118,16 +99,18 @@ int renderChart (
{ {
std::cout << "No filtered data found"; std::cout << "No filtered data found";
if (filter.is_started ()) if (range.is_started ())
{ {
std::cout << " in the range " << filter.start.toISOLocalExtended (); std::cout << " in the range " << range.start.toISOLocalExtended ();
if (filter.is_ended ()) if (range.is_ended ())
std::cout << " - " << filter.end.toISOLocalExtended (); {
std::cout << " - " << range.end.toISOLocalExtended ();
}
} }
if (! filter.tags ().empty ()) if (! tags.empty ())
{ {
std::cout << " tagged with " << joinQuotedIfNeeded (", ", filter.tags ()); std::cout << " tagged with " << joinQuotedIfNeeded (", ", tags);
} }
std::cout << ".\n"; std::cout << ".\n";
@ -136,8 +119,8 @@ int renderChart (
return 0; return 0;
} }
const auto exclusions = getAllExclusions (rules, filter); const auto exclusions = getAllExclusions (rules, range);
const auto holidays = createHolidayMap (rules, filter); const auto holidays = createHolidayMap (rules, range);
// Map tags to colors. // Map tags to colors.
auto palette = createPalette (rules); auto palette = createPalette (rules);
@ -146,12 +129,16 @@ int renderChart (
const auto minutes_per_char = rules.getInteger ("reports." + type + ".cell"); const auto minutes_per_char = rules.getInteger ("reports." + type + ".cell");
if (minutes_per_char < 1) if (minutes_per_char < 1)
{
throw format ("The value for 'reports.{1}.cell' must be at least 1.", type); throw format ("The value for 'reports.{1}.cell' must be at least 1.", type);
}
const auto num_lines = rules.getInteger ("reports." + type + ".lines", 1); const auto num_lines = rules.getInteger ("reports." + type + ".lines", 1);
if (num_lines < 1) if (num_lines < 1)
{
throw format ("Invalid value for 'reports.{1}.lines': '{2}'", type, num_lines); throw format ("Invalid value for 'reports.{1}.lines': '{2}'", type, num_lines);
}
ChartConfig configuration {}; ChartConfig configuration {};
configuration.reference_datetime = Datetime (); configuration.reference_datetime = Datetime ();
@ -177,13 +164,13 @@ int renderChart (
Chart chart (configuration); Chart chart (configuration);
std::cout << chart.render (filter, tracked, exclusions, holidays); std::cout << chart.render (range, tracked, exclusions, holidays);
return 0; return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::map <Datetime, std::string> createHolidayMap (Rules &rules, Interval &filter) std::map <Datetime, std::string> createHolidayMap (Rules& rules, Range& range)
{ {
std::map <Datetime, std::string> mapping; std::map <Datetime, std::string> mapping;
auto holidays = rules.all ("holidays."); auto holidays = rules.all ("holidays.");
@ -199,7 +186,7 @@ std::map <Datetime, std::string> createHolidayMap (Rules &rules, Interval &filte
std::replace (date.begin (), date.end (), '_', '-'); std::replace (date.begin (), date.end (), '_', '-');
Datetime holiday (date); Datetime holiday (date);
if (holiday >= filter.start && holiday <= filter.end) if (holiday >= range.start && holiday <= range.end)
{ {
std::stringstream out; std::stringstream out;
out << " [" out << " ["

View file

@ -44,16 +44,17 @@ int CmdContinue (
const bool verbose = rules.getBoolean ("verbose"); const bool verbose = rules.getBoolean ("verbose");
const Datetime now {}; const Datetime now {};
auto filter = cli.getFilter ({ now, 0 }); auto range = cli.getRange ({now, 0});
if (filter.start > now) if (range.start > now)
{ {
throw std::string ("Time tracking cannot be set in the future."); throw std::string ("Time tracking cannot be set in the future.");
} }
std::set <int> ids = cli.getIds (); auto tags = cli.getTags ();
auto ids = cli.getIds ();
if (!ids.empty () && !filter.tags().empty ()) if (! ids.empty () && ! tags.empty ())
{ {
throw std::string ("You cannot specify both id and tags to continue an interval."); throw std::string ("You cannot specify both id and tags to continue an interval.");
} }
@ -74,14 +75,14 @@ int CmdContinue (
throw format ("ID '@{1}' does not correspond to any tracking.", *ids.begin ()); throw format ("ID '@{1}' does not correspond to any tracking.", *ids.begin ());
} }
} }
else if (!filter.tags ().empty ()) else if (!tags.empty ())
{ {
IntervalFilterFirstOf filtering {std::make_shared <IntervalFilterAllWithTags> (filter.tags ())}; IntervalFilterFirstOf filtering {std::make_shared <IntervalFilterAllWithTags> (tags)};
intervals = getTracked (database, rules, filtering); intervals = getTracked (database, rules, filtering);
if (intervals.empty ()) if (intervals.empty ())
{ {
throw format ("Tags '{1}' do not correspond to any tracking.", joinQuotedIfNeeded (", ", filter.tags ())); throw format ("Tags '{1}' do not correspond to any tracking.", joinQuotedIfNeeded (", ", tags));
} }
} }
else else
@ -106,10 +107,10 @@ int CmdContinue (
Datetime start_time; Datetime start_time;
Datetime end_time; Datetime end_time;
if (filter.is_started ()) if (range.is_started ())
{ {
start_time = filter.start; start_time = range.start;
end_time = filter.end; end_time = range.end;
} }
else else
{ {

View file

@ -39,8 +39,7 @@ int CmdDelete (
{ {
const bool verbose = rules.getBoolean ("verbose"); const bool verbose = rules.getBoolean ("verbose");
// Gather IDs. auto ids = cli.getIds ();
std::set <int> ids = cli.getIds ();
if (ids.empty ()) if (ids.empty ())
{ {
@ -53,7 +52,6 @@ int CmdDelete (
auto filtering = IntervalFilterAllWithIds (ids); auto filtering = IntervalFilterAllWithIds (ids);
auto intervals = getTracked (database, rules, filtering); auto intervals = getTracked (database, rules, filtering);
if (intervals.size () != ids.size ()) if (intervals.size () != ids.size ())
{ {
for (auto& id: ids) for (auto& id: ids)

View file

@ -38,16 +38,19 @@ int CmdExport (
Rules& rules, Rules& rules,
Database& database) Database& database)
{ {
auto filter = cli.getFilter (); auto ids = cli.getIds ();
std::set <int> ids = cli.getIds (); auto range = cli.getRange ();
auto tags = cli.getTags ();
std::shared_ptr <IntervalFilter> filtering; std::shared_ptr <IntervalFilter> filtering;
if (!ids.empty ()) if (! ids.empty ())
{ {
if (!filter.empty ()) if (! range.is_empty ())
{ {
throw std::string ("You cannot specify both id and tags/range to export intervals."); throw std::string ("You cannot specify both id and tags/range to export intervals.");
} }
filtering = std::make_shared <IntervalFilterAllWithIds> (ids); filtering = std::make_shared <IntervalFilterAllWithIds> (ids);
} }
else else
@ -55,14 +58,15 @@ int CmdExport (
filtering = std::make_shared <IntervalFilterAndGroup> ( filtering = std::make_shared <IntervalFilterAndGroup> (
std::vector <std::shared_ptr <IntervalFilter>> ( std::vector <std::shared_ptr <IntervalFilter>> (
{ {
std::make_shared <IntervalFilterAllInRange> (Range{filter.start, filter.end}), std::make_shared <IntervalFilterAllInRange> (range),
std::make_shared <IntervalFilterAllWithTags> (filter.tags ()), std::make_shared <IntervalFilterAllWithTags> (tags),
} }
) )
); );
} }
auto intervals = getTracked (database, rules, *filtering); auto intervals = getTracked (database, rules, *filtering);
std::cout << jsonFromIntervals (intervals); std::cout << jsonFromIntervals (intervals);
return 0; return 0;

View file

@ -40,7 +40,7 @@ int CmdFill (
{ {
const bool verbose = rules.getBoolean ("verbose"); const bool verbose = rules.getBoolean ("verbose");
std::set <int> ids = cli.getIds (); auto ids = cli.getIds ();
if (ids.empty ()) if (ids.empty ())
{ {
@ -48,10 +48,7 @@ int CmdFill (
} }
// Load the data. // Load the data.
// Note: There is no filter. auto filtering = IntervalFilterAllInRange ({0, 0});
Interval filter;
auto filtering = IntervalFilterAllInRange ({ 0, 0 });
auto tracked = getTracked (database, rules, filtering); auto tracked = getTracked (database, rules, filtering);
journal.startTransaction (); journal.startTransaction ();
@ -60,7 +57,9 @@ int CmdFill (
for (auto& id : ids) for (auto& id : ids)
{ {
if (id > static_cast <int> (tracked.size ())) if (id > static_cast <int> (tracked.size ()))
{
throw format ("ID '@{1}' does not correspond to any tracking.", id); throw format ("ID '@{1}' does not correspond to any tracking.", id);
}
Interval from = tracked[tracked.size () - id]; Interval from = tracked[tracked.size () - id];
std::cout << "# from " << from.dump () << "\n"; std::cout << "# from " << from.dump () << "\n";

View file

@ -45,16 +45,22 @@ int CmdGaps (
Range default_range = {}; Range default_range = {};
expandIntervalHint (":" + report_hint, default_range); expandIntervalHint (":" + report_hint, default_range);
auto filter = cli.getFilter (default_range); auto range = cli.getRange (default_range);
auto tags = cli.getTags ();
auto filter = Interval {range, tags};
// Is the :blank hint being used? // Is the :blank hint being used?
bool blank = cli.getHint ("blank", false); bool blank = cli.getHint ("blank", false);
std::vector <Range> untracked; std::vector <Range> untracked;
if (blank) if (blank)
untracked = subtractRanges ({filter}, getAllExclusions (rules, filter)); {
untracked = subtractRanges ({range}, getAllExclusions (rules, range));
}
else else
{
untracked = getUntracked (database, rules, filter); untracked = getUntracked (database, rules, filter);
}
Table table; Table table;
table.width (1024); table.width (1024);
@ -70,7 +76,7 @@ int CmdGaps (
// Each day is rendered separately. // Each day is rendered separately.
time_t grand_total = 0; time_t grand_total = 0;
Datetime previous; Datetime previous;
for (Datetime day = filter.start; day < filter.end; day++) for (Datetime day = range.start; day < range.end; day++)
{ {
auto day_range = getFullDay (day); auto day_range = getFullDay (day);
time_t daily_total = 0; time_t daily_total = 0;

View file

@ -37,9 +37,10 @@ int CmdGet (
Rules& rules, Rules& rules,
Database& database) Database& database)
{ {
auto references = cli.getDomReferences ();
auto filter = Interval {cli.getRange (), cli.getTags()};
std::vector <std::string> results; std::vector <std::string> results;
std::vector <std::string> references = cli.getDomReferences ();
Interval filter = cli.getFilter ();
for (auto& reference : references) for (auto& reference : references)
{ {

View file

@ -41,7 +41,7 @@ int CmdJoin (
const bool verbose = rules.getBoolean ("verbose"); const bool verbose = rules.getBoolean ("verbose");
// Gather IDs and TAGs. // Gather IDs and TAGs.
std::set <int> ids = cli.getIds (); auto ids = cli.getIds ();
// Only 2 IDs allowed in a join. // Only 2 IDs allowed in a join.
if (ids.size () != 2) if (ids.size () != 2)

View file

@ -40,15 +40,14 @@ int CmdLengthen (
{ {
const bool verbose = rules.getBoolean ("verbose"); const bool verbose = rules.getBoolean ("verbose");
// Gather IDs and TAGs. auto ids = cli.getIds ();
std::set <int> ids = cli.getIds ();
if (ids.empty ()) if (ids.empty ())
{ {
throw std::string ("IDs must be specified. See 'timew help lengthen'."); throw std::string ("IDs must be specified. See 'timew help lengthen'.");
} }
Duration dur = cli.getDuration (); auto dur = cli.getDuration ();
journal.startTransaction (); journal.startTransaction ();

View file

@ -39,10 +39,9 @@ int CmdModify (
{ {
const bool verbose = rules.getBoolean ("verbose"); const bool verbose = rules.getBoolean ("verbose");
auto filter = cli.getFilter (); auto words = cli.getWords ();
std::set <int> ids = cli.getIds ();
std::vector <std::string> words = cli.getWords (); enum {MODIFY_START, MODIFY_END} op = MODIFY_START;
enum { MODIFY_START, MODIFY_END } op = MODIFY_START;
if (words.empty()) if (words.empty())
{ {
@ -62,6 +61,8 @@ int CmdModify (
throw format ("Must specify start|end command to modify. See 'timew help modify'.", words.at (0)); throw format ("Must specify start|end command to modify. See 'timew help modify'.", words.at (0));
} }
auto ids = cli.getIds ();
if (ids.empty ()) if (ids.empty ())
{ {
throw std::string ("ID must be specified. See 'timew help modify'."); throw std::string ("ID must be specified. See 'timew help modify'.");
@ -72,6 +73,8 @@ int CmdModify (
throw std::string ("Only one ID may be specified. See 'timew help modify'."); throw std::string ("Only one ID may be specified. See 'timew help modify'.");
} }
auto range = cli.getRange ({0, 0});
int id = *ids.begin(); int id = *ids.begin();
flattenDatabase (database, rules); flattenDatabase (database, rules);
@ -84,7 +87,7 @@ int CmdModify (
} }
assert (intervals.size () == 1); assert (intervals.size () == 1);
if (! filter.is_started ()) if (! range.is_started ())
{ {
throw std::string ("No updated time specified. See 'timew help modify'."); throw std::string ("No updated time specified. See 'timew help modify'.");
} }
@ -94,7 +97,7 @@ int CmdModify (
switch (op) switch (op)
{ {
case MODIFY_START: case MODIFY_START:
modified.start = filter.start; modified.start = range.start;
break; break;
case MODIFY_END: case MODIFY_END:
@ -102,7 +105,7 @@ int CmdModify (
{ {
throw format ("Cannot modify end of open interval @{1}.", id); throw format ("Cannot modify end of open interval @{1}.", id);
} }
modified.end = filter.start; modified.end = range.start;
break; break;
} }

View file

@ -134,24 +134,28 @@ int CmdReport (
expandIntervalHint (":" + report_hint, default_range); expandIntervalHint (":" + report_hint, default_range);
// Create a filter, and if empty, choose the current week. // Create a filter, and if empty, choose the current week.
auto filter = cli.getFilter (default_range); auto tags = cli.getTags ();
auto range = cli.getRange (default_range);
IntervalFilterAndGroup filtering ({ IntervalFilterAndGroup filtering ({
std::make_shared <IntervalFilterAllInRange> ( Range { filter.start, filter.end }), std::make_shared <IntervalFilterAllInRange> (range),
std::make_shared <IntervalFilterAllWithTags> (filter.tags ()) std::make_shared <IntervalFilterAllWithTags> (tags)
}); });
auto tracked = getTracked (database, rules, filtering); auto tracked = getTracked (database, rules, filtering);
// Compose Header info. // Compose Header info.
rules.set ("temp.report.start", filter.is_started () ? filter.start.toISO () : ""); rules.set ("temp.report.start", range.is_started () ? range.start.toISO () : "");
rules.set ("temp.report.end", filter.is_ended () ? filter.end.toISO () : ""); rules.set ("temp.report.end", range.is_ended () ? range.end.toISO () : "");
rules.set ("temp.report.tags", joinQuotedIfNeeded (",", filter.tags ())); rules.set ("temp.report.tags", joinQuotedIfNeeded (",", tags));
rules.set ("temp.version", VERSION); rules.set ("temp.version", VERSION);
std::stringstream header; std::stringstream header;
for (auto& name : rules.all ()) for (auto& name : rules.all ())
{
header << name << ": " << rules.get (name) << '\n'; header << name << ": " << rules.get (name) << '\n';
}
// Get the data. // Get the data.
auto input = header.str () auto input = header.str ()
@ -168,7 +172,9 @@ int CmdReport (
// Display the output. // Display the output.
for (auto& line : output) for (auto& line : output)
{
std::cout << line << '\n'; std::cout << line << '\n';
}
return rc; return rc;
} }

View file

@ -38,13 +38,14 @@ int CmdStart (
const bool verbose = rules.getBoolean ("verbose"); const bool verbose = rules.getBoolean ("verbose");
const Datetime now {}; const Datetime now {};
auto interval = cli.getFilter ({ now, 0 }); auto range = cli.getRange ({now, 0});
auto tags = cli.getTags ();
if (interval.start > now) if (range.start > now)
{ {
throw std::string ("Time tracking cannot be set in the future."); throw std::string ("Time tracking cannot be set in the future.");
} }
else if (!interval.is_started () || interval.is_ended ()) else if (! range.is_started () || range.is_ended ())
{ {
throw std::string ("The start command does not accept ranges but only a single datetime. " throw std::string ("The start command does not accept ranges but only a single datetime. "
"Perhaps you want the track command?"); "Perhaps you want the track command?");
@ -58,6 +59,8 @@ int CmdStart (
} }
journal.startTransaction (); journal.startTransaction ();
auto interval = Interval {range, tags};
if (validate (cli, rules, database, interval)) if (validate (cli, rules, database, interval))
{ {
database.addInterval (interval, verbose); database.addInterval (interval, verbose);

View file

@ -50,7 +50,6 @@ int CmdStop (
const bool verbose = rules.getBoolean ("verbose"); const bool verbose = rules.getBoolean ("verbose");
const Datetime now {}; const Datetime now {};
auto filter = cli.getFilter ({ now, 0 });
// Load the most recent interval. // Load the most recent interval.
auto latest = getLatestInterval (database); auto latest = getLatestInterval (database);
@ -67,30 +66,34 @@ int CmdStop (
"Perhaps you want the modify command?."); "Perhaps you want the modify command?.");
} }
if (! filter.is_started()) auto range = cli.getRange ({now, 0});
if (! range.is_started ())
{ {
throw std::string ("No datetime specified."); throw std::string ("No datetime specified.");
} }
else if (filter.start <= latest.start) else if (range.start <= latest.start)
{ {
throw std::string ("The end of a date range must be after the start."); throw std::string ("The end of a date range must be after the start.");
} }
auto tags = cli.getTags ();
std::set <std::string> diff = {}; std::set <std::string> diff = {};
if(! std::includes(latest.tags ().begin (), latest.tags ().end (), if(! std::includes(latest.tags ().begin (), latest.tags ().end (),
filter.tags ().begin (), filter.tags ().end ())) tags.begin (), tags.end ()))
{ {
std::set_difference(filter.tags ().begin (), filter.tags ().end (), std::set_difference(tags.begin (), tags.end (),
latest.tags ().begin (), latest.tags ().end (), latest.tags ().begin (), latest.tags ().end (),
std::inserter(diff, diff.begin ())); std::inserter(diff, diff.begin ()));
throw format ("The current interval does not have the '{1}' tag.", *diff.begin ()); throw format ("The current interval does not have the '{1}' tag.", *diff.begin ());
} }
else if (!filter.tags ().empty ()) else if (! tags.empty ())
{ {
std::set_difference(latest.tags ().begin (), latest.tags ().end (), std::set_difference(latest.tags ().begin (), latest.tags ().end (),
filter.tags ().begin (), filter.tags ().end (), tags.begin (), tags.end (),
std::inserter(diff, diff.begin())); std::inserter(diff, diff.begin()));
} }
@ -98,8 +101,8 @@ int CmdStop (
if (diff.empty ()) if (diff.empty ())
{ {
Interval modified { latest }; Interval modified {latest};
modified.end = filter.start; modified.end = range.start;
database.deleteInterval (latest); database.deleteInterval (latest);
validate (cli, rules, database, modified); validate (cli, rules, database, modified);
@ -116,7 +119,7 @@ int CmdStop (
} }
else else
{ {
Interval next { filter.start, 0 }; Interval next {range.start, 0};
for (auto& tag : diff) for (auto& tag : diff)
{ {
next.tag (tag); next.tag (tag);

View file

@ -36,7 +36,7 @@
#include <utf8.h> #include <utf8.h>
// Implemented in CmdChart.cpp. // Implemented in CmdChart.cpp.
std::map <Datetime, std::string> createHolidayMap (Rules&, Interval&); std::map <Datetime, std::string> createHolidayMap (Rules&, Range&);
std::string renderHolidays (const std::map <Datetime, std::string>&); std::string renderHolidays (const std::map <Datetime, std::string>&);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -53,12 +53,13 @@ int CmdSummary (
Range default_range = {}; Range default_range = {};
expandIntervalHint (":" + report_hint, default_range); expandIntervalHint (":" + report_hint, default_range);
auto filter = cli.getFilter (default_range); auto range = cli.getRange (default_range);
auto tags = cli.getTags ();
// Load the data. // Load the data.
IntervalFilterAndGroup filtering ({ IntervalFilterAndGroup filtering ({
std::make_shared <IntervalFilterAllInRange> ( Range { filter.start, filter.end }), std::make_shared <IntervalFilterAllInRange> (range),
std::make_shared <IntervalFilterAllWithTags> (filter.tags()) std::make_shared <IntervalFilterAllWithTags> (tags)
}); });
auto tracked = getTracked (database, rules, filtering); auto tracked = getTracked (database, rules, filtering);
@ -69,16 +70,18 @@ int CmdSummary (
{ {
std::cout << "No filtered data found"; std::cout << "No filtered data found";
if (filter.is_started ()) if (range.is_started ())
{ {
std::cout << " in the range " << filter.start.toISOLocalExtended (); std::cout << " in the range " << range.start.toISOLocalExtended ();
if (filter.is_ended ()) if (range.is_ended ())
std::cout << " - " << filter.end.toISOLocalExtended (); {
std::cout << " - " << range.end.toISOLocalExtended ();
}
} }
if (! filter.tags ().empty ()) if (! tags.empty ())
{ {
std::cout << " tagged with " << joinQuotedIfNeeded (", ", filter.tags ()); std::cout << " tagged with " << joinQuotedIfNeeded (", ", tags);
} }
std::cout << ".\n"; std::cout << ".\n";
@ -159,8 +162,8 @@ int CmdSummary (
time_t grand_total = 0; time_t grand_total = 0;
Datetime previous; Datetime previous;
auto days_start = filter.is_started() ? filter.start : tracked.front ().start; auto days_start = range.is_started() ? range.start : tracked.front ().start;
auto days_end = filter.is_ended() ? filter.end : tracked.back ().end; auto days_end = range.is_ended() ? range.end : tracked.back ().end;
const auto now = Datetime (); const auto now = Datetime ();
@ -221,8 +224,8 @@ int CmdSummary (
if (show_tags) if (show_tags)
{ {
std::string tags = join (", ", track.tags ()); std::string tags_string = join (", ", track.tags ());
table.set (row, tags_col_index, tags, summaryIntervalColor (rules, track.tags ())); table.set (row, tags_col_index, tags_string, summaryIntervalColor (rules, track.tags ()));
} }
if (show_annotations) if (show_annotations)
@ -260,7 +263,7 @@ int CmdSummary (
std::cout << '\n' std::cout << '\n'
<< table.render () << table.render ()
<< (show_holidays ? renderHolidays (createHolidayMap (rules, filter)) : "") << (show_holidays ? renderHolidays (createHolidayMap (rules, range)) : "")
<< '\n'; << '\n';
return 0; return 0;

View file

@ -43,7 +43,7 @@ int CmdTag (
// Gather IDs and TAGs. // Gather IDs and TAGs.
std::set <int> ids = cli.getIds (); std::set <int> ids = cli.getIds ();
std::vector<std::string> tags = cli.getTags (); std::set<std::string> tags = cli.getTags ();
if (tags.empty ()) if (tags.empty ())
{ {

View file

@ -42,15 +42,14 @@ int CmdTags (
{ {
const bool verbose = rules.getBoolean ("verbose"); const bool verbose = rules.getBoolean ("verbose");
// Create a filter, with no default range.
auto filter = cli.getFilter ();
IntervalFilterAndGroup filtering ({ IntervalFilterAndGroup filtering ({
std::make_shared <IntervalFilterAllInRange> ( Range { filter.start, filter.end }), std::make_shared <IntervalFilterAllInRange> (cli.getRange ()),
std::make_shared <IntervalFilterAllWithTags> (filter.tags ()) std::make_shared <IntervalFilterAllWithTags> (cli.getTags ())
}); });
// Generate a unique, ordered list of tags. // Generate a unique, ordered list of tags.
std::set <std::string> tags; std::set <std::string> tags;
for (const auto& interval : getTracked (database, rules, filtering)) for (const auto& interval : getTracked (database, rules, filtering))
for (auto& tag : interval.tags ()) for (auto& tag : interval.tags ())
tags.insert (tag); tags.insert (tag);

View file

@ -37,8 +37,6 @@ int CmdTrack (
{ {
const bool verbose = rules.getBoolean ("verbose"); const bool verbose = rules.getBoolean ("verbose");
auto filter = cli.getFilter ();
// We expect no ids // We expect no ids
if (! cli.getIds ().empty ()) if (! cli.getIds ().empty ())
{ {
@ -46,23 +44,32 @@ int CmdTrack (
"Perhaps you want the continue command?"); "Perhaps you want the continue command?");
} }
auto range = cli.getRange ();
// If this is not a proper closed interval, then the user is trying to make // If this is not a proper closed interval, then the user is trying to make
// the 'track' command behave like 'start', so delegate to CmdStart. // the 'track' command behave like 'start', so delegate to CmdStart.
if (! filter.is_started () || if (! range.is_started () || ! range.is_ended ())
! filter.is_ended ()) {
return CmdStart (cli, rules, database, journal); return CmdStart (cli, rules, database, journal);
}
auto tags = cli.getTags ();
journal.startTransaction (); journal.startTransaction ();
auto filter = Interval {range, tags};
// Validation must occur before flattening. // Validation must occur before flattening.
validate (cli, rules, database, filter); validate (cli, rules, database, filter);
for (auto& interval : flatten (filter, getAllExclusions (rules, filter))) for (auto& interval : flatten (filter, getAllExclusions (rules, range)))
{ {
database.addInterval (interval, verbose); database.addInterval (interval, verbose);
if (verbose) if (verbose)
{
std::cout << intervalSummarize (rules, interval); std::cout << intervalSummarize (rules, interval);
}
} }
journal.endTransaction (); journal.endTransaction ();

View file

@ -43,7 +43,7 @@ int CmdUntag (
// Gather IDs and TAGs. // Gather IDs and TAGs.
std::set <int> ids = cli.getIds (); std::set <int> ids = cli.getIds ();
std::vector<std::string> tags = cli.getTags (); std::set<std::string> tags = cli.getTags ();
if (tags.empty ()) if (tags.empty ())
{ {

View file

@ -119,8 +119,8 @@ bool domGet (
else if (pig.skipLiteral ("tracked.")) else if (pig.skipLiteral ("tracked."))
{ {
IntervalFilterAndGroup filtering ({ IntervalFilterAndGroup filtering ({
std::make_shared <IntervalFilterAllInRange> ( Range { filter.start, filter.end }), std::make_shared <IntervalFilterAllInRange> (Range {filter.start, filter.end}),
std::make_shared <IntervalFilterAllWithTags> (filter.tags()) std::make_shared <IntervalFilterAllWithTags> (filter.tags ())
}); });
auto tracked = getTracked (database, rules, filtering); auto tracked = getTracked (database, rules, filtering);

View file

@ -124,7 +124,7 @@ class TestTag(TestCase):
code, out, err = self.t("tag @1 foo bar") code, out, err = self.t("tag @1 foo bar")
self.assertIn("Added foo bar to @1", out) self.assertIn("Added bar foo to @1", out)
j = self.t.export() j = self.t.export()
self.assertOpenInterval(j[0], expectedTags=["bar", "foo"]) self.assertOpenInterval(j[0], expectedTags=["bar", "foo"])
@ -138,7 +138,7 @@ class TestTag(TestCase):
code, out, err = self.t("tag @1 foo bar") code, out, err = self.t("tag @1 foo bar")
self.assertIn("Added foo bar to @1", out) self.assertIn("Added bar foo to @1", out)
j = self.t.export() j = self.t.export()
self.assertClosedInterval(j[0], expectedTags=["bar", "foo"]) self.assertClosedInterval(j[0], expectedTags=["bar", "foo"])
@ -171,7 +171,7 @@ class TestTag(TestCase):
code, out, err = self.t("tag @1 @2 foo bar") code, out, err = self.t("tag @1 @2 foo bar")
self.assertIn("Added foo bar to @2\nAdded foo bar to @1", out) self.assertIn("Added bar foo to @2\nAdded bar foo to @1", out)
j = self.t.export() j = self.t.export()
self.assertClosedInterval(j[0], expectedTags=["bar", "foo", "one"]) self.assertClosedInterval(j[0], expectedTags=["bar", "foo", "one"])

View file

@ -124,7 +124,7 @@ class TestUntag(TestCase):
code, out, err = self.t("untag @1 foo bar") code, out, err = self.t("untag @1 foo bar")
self.assertIn('Removed foo bar from @1', out) self.assertIn('Removed bar foo from @1', out)
j = self.t.export() j = self.t.export()
self.assertOpenInterval(j[0], expectedTags=["baz"]) self.assertOpenInterval(j[0], expectedTags=["baz"])
@ -138,7 +138,7 @@ class TestUntag(TestCase):
code, out, err = self.t("untag @1 foo bar") code, out, err = self.t("untag @1 foo bar")
self.assertIn('Removed foo bar from @1', out) self.assertIn('Removed bar foo from @1', out)
j = self.t.export() j = self.t.export()
self.assertClosedInterval(j[0], expectedTags=["baz"]) self.assertClosedInterval(j[0], expectedTags=["baz"])
@ -171,7 +171,7 @@ class TestUntag(TestCase):
code, out, err = self.t("untag @1 @2 foo bar") code, out, err = self.t("untag @1 @2 foo bar")
self.assertIn('Removed foo bar from @2\nRemoved foo bar from @1', out) self.assertIn('Removed bar foo from @2\nRemoved bar foo from @1', out)
j = self.t.export() j = self.t.export()
self.assertClosedInterval(j[0], expectedTags=["one"]) self.assertClosedInterval(j[0], expectedTags=["one"])