mirror of
https://github.com/GothenburgBitFactory/timewarrior.git
synced 2025-06-26 10:54:28 +02:00
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:
parent
0c84dfd42e
commit
34bba44d38
28 changed files with 221 additions and 219 deletions
82
src/CLI.cpp
82
src/CLI.cpp
|
@ -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)
|
||||
{
|
||||
if (arg.hasTag ("TAG"))
|
||||
tags.push_back (arg.attribute ("raw"));
|
||||
tags.insert (arg.attribute ("raw"));
|
||||
}
|
||||
|
||||
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> "for" <duration>
|
||||
// <duration> ["before"|"after" <date>]
|
||||
// <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;
|
||||
|
||||
Interval filter;
|
||||
Range the_range;
|
||||
std::string start;
|
||||
std::string end;
|
||||
std::string duration;
|
||||
|
@ -666,16 +664,16 @@ Interval CLI::getFilter (const Range& default_range) const
|
|||
{
|
||||
if (range.is_empty ())
|
||||
{
|
||||
args.emplace_back ("<all>");
|
||||
args.emplace_back("<all>");
|
||||
}
|
||||
else
|
||||
{
|
||||
start = range.start.toISO ();
|
||||
end = range.end.toISO ();
|
||||
|
||||
args.emplace_back ("<date>");
|
||||
args.emplace_back ("-");
|
||||
args.emplace_back ("<date>");
|
||||
args.emplace_back("<date>");
|
||||
args.emplace_back("-");
|
||||
args.emplace_back("<date>");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -688,14 +686,14 @@ Interval CLI::getFilter (const Range& default_range) const
|
|||
else if (end.empty ())
|
||||
end = raw;
|
||||
|
||||
args.emplace_back ("<date>");
|
||||
args.emplace_back("<date>");
|
||||
}
|
||||
else if (arg._lextype == Lexer::Type::duration)
|
||||
{
|
||||
if (duration.empty ())
|
||||
duration = raw;
|
||||
|
||||
args.emplace_back ("<duration>");
|
||||
args.emplace_back("<duration>");
|
||||
}
|
||||
else if (arg.hasTag ("KEYWORD"))
|
||||
{
|
||||
|
@ -704,37 +702,27 @@ Interval CLI::getFilter (const Range& default_range) const
|
|||
// function.
|
||||
args.push_back (raw);
|
||||
}
|
||||
else if (arg.hasTag ("ID"))
|
||||
{
|
||||
// Not part of a filter.
|
||||
}
|
||||
else
|
||||
{
|
||||
filter.tag (raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args.empty ())
|
||||
{
|
||||
filter.setRange(default_range);
|
||||
the_range = default_range;
|
||||
}
|
||||
|
||||
// <date>
|
||||
else if (args.size () == 1 &&
|
||||
args[0] == "<date>")
|
||||
args[0] == "<date>")
|
||||
{
|
||||
DatetimeParser dtp;
|
||||
Range range = dtp.parse_range(start);
|
||||
filter.setRange (range);
|
||||
the_range = Range (range);
|
||||
}
|
||||
|
||||
// from <date>
|
||||
else if (args.size () == 2 &&
|
||||
args[0] == "from" &&
|
||||
args[1] == "<date>")
|
||||
{
|
||||
filter.setRange ({Datetime (start), 0});
|
||||
the_range = Range ({Datetime (start), 0});
|
||||
}
|
||||
// <date> to/- <date>
|
||||
else if (args.size () == 3 &&
|
||||
|
@ -742,9 +730,8 @@ Interval CLI::getFilter (const Range& default_range) const
|
|||
(args[1] == "to" || args[1] == "-") &&
|
||||
args[2] == "<date>")
|
||||
{
|
||||
filter.setRange ({Datetime (start), Datetime (end)});
|
||||
the_range = Range ({Datetime (start), Datetime (end)});
|
||||
}
|
||||
|
||||
// from <date> to/- <date>
|
||||
else if (args.size () == 4 &&
|
||||
args[0] == "from" &&
|
||||
|
@ -752,18 +739,16 @@ Interval CLI::getFilter (const Range& default_range) const
|
|||
(args[2] == "to" || args[2] == "-") &&
|
||||
args[3] == "<date>")
|
||||
{
|
||||
filter.setRange ({Datetime (start), Datetime (end)});
|
||||
the_range = Range ({Datetime (start), Datetime (end)});
|
||||
}
|
||||
|
||||
// <date> for <duration>
|
||||
else if (args.size () == 3 &&
|
||||
args[0] == "<date>" &&
|
||||
args[1] == "for" &&
|
||||
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>
|
||||
else if (args.size () == 4 &&
|
||||
args[0] == "from" &&
|
||||
|
@ -771,66 +756,61 @@ Interval CLI::getFilter (const Range& default_range) const
|
|||
args[2] == "for" &&
|
||||
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>
|
||||
else if (args.size () == 3 &&
|
||||
args[0] == "<duration>" &&
|
||||
args[1] == "before" &&
|
||||
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>
|
||||
else if (args.size () == 3 &&
|
||||
args[0] == "<duration>" &&
|
||||
args[1] == "after" &&
|
||||
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
|
||||
else if (args.size () == 2 &&
|
||||
args[0] == "<duration>" &&
|
||||
args[1] == "ago")
|
||||
{
|
||||
filter.setRange ({now - Duration (duration).toTime_t (), 0});
|
||||
the_range = Range ({now - Duration (duration).toTime_t (), 0});
|
||||
}
|
||||
|
||||
// for <duration>
|
||||
else if (args.size () == 2 &&
|
||||
args[0] == "for" &&
|
||||
args[1] == "<duration>")
|
||||
{
|
||||
filter.setRange ({now - Duration (duration).toTime_t (), now});
|
||||
the_range = Range ({now - Duration (duration).toTime_t (), now});
|
||||
}
|
||||
|
||||
// <duration>
|
||||
else if (args.size () == 1 &&
|
||||
args[0] == "<duration>")
|
||||
{
|
||||
filter.setRange ({now - Duration (duration).toTime_t (), now});
|
||||
the_range = Range ({now - Duration (duration).toTime_t (), now});
|
||||
}
|
||||
|
||||
// :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 ())
|
||||
{
|
||||
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.");
|
||||
}
|
||||
|
||||
return filter;
|
||||
return the_range;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -70,11 +70,11 @@ public:
|
|||
bool getComplementaryHint (const std::string&, bool) const;
|
||||
bool getHint(const std::string&, bool) const;
|
||||
std::set <int> getIds () const;
|
||||
std::vector<std::string> getTags () const;
|
||||
std::set<std::string> getTags () const;
|
||||
std::string getAnnotation() const;
|
||||
Duration getDuration() 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;
|
||||
|
||||
private:
|
||||
|
|
|
@ -61,14 +61,14 @@ Chart::Chart (const ChartConfig& configuration) :
|
|||
{ }
|
||||
|
||||
std::string Chart::render (
|
||||
const Interval &filter,
|
||||
const Range& range,
|
||||
const std::vector<Interval> &tracked,
|
||||
const std::vector<Range> &exclusions,
|
||||
const std::map<Datetime, std::string> &holidays)
|
||||
{
|
||||
// Determine hours shown.
|
||||
auto hour_range = determine_hour_range
|
||||
? determineHourRange (filter, tracked)
|
||||
? determineHourRange (range, tracked)
|
||||
: std::make_pair (0, 23);
|
||||
|
||||
int first_hour = hour_range.first;
|
||||
|
@ -97,7 +97,7 @@ std::string Chart::render (
|
|||
// Each day is rendered separately.
|
||||
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.
|
||||
|
||||
|
@ -154,7 +154,7 @@ std::string Chart::render (
|
|||
|
||||
out << (with_totals ? renderSubTotal (total_work, std::string (padding_size, ' ')) : "")
|
||||
<< (with_holidays ? renderHolidays (holidays) : "")
|
||||
<< (with_summary ? renderSummary (indent, filter, exclusions, tracked) : "");
|
||||
<< (with_summary ? renderSummary (indent, range, exclusions, tracked) : "");
|
||||
|
||||
return out.str ();
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ unsigned long Chart::getIndentSize ()
|
|||
// Scan all tracked intervals, looking for the earliest and latest hour into
|
||||
// which an interval extends.
|
||||
std::pair<int, int> Chart::determineHourRange (
|
||||
const Interval &filter,
|
||||
const Range& range,
|
||||
const std::vector<Interval> &tracked)
|
||||
{
|
||||
// If there is no data, show the whole day.
|
||||
|
@ -185,11 +185,11 @@ std::pair<int, int> Chart::determineHourRange (
|
|||
auto first_hour = 23;
|
||||
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);
|
||||
|
||||
for (auto &track : tracked)
|
||||
for (auto& track : tracked)
|
||||
{
|
||||
Interval test {track};
|
||||
|
||||
|
@ -549,7 +549,7 @@ std::string Chart::renderHolidays (const std::map<Datetime, std::string> &holida
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string Chart::renderSummary (
|
||||
const std::string &indent,
|
||||
const Interval &filter,
|
||||
const Range& range,
|
||||
const std::vector<Range> &exclusions,
|
||||
const std::vector<Interval> &tracked)
|
||||
{
|
||||
|
@ -558,9 +558,9 @@ std::string Chart::renderSummary (
|
|||
|
||||
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)
|
||||
{
|
||||
if (filter.overlaps (interval))
|
||||
if (range.overlaps (interval))
|
||||
{
|
||||
Interval clipped = clip (interval, filter);
|
||||
Interval clipped = clip (interval, range);
|
||||
if (interval.is_open ())
|
||||
{
|
||||
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);
|
||||
auto total_remaining = total_available - total_worked;
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ class Chart
|
|||
public:
|
||||
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:
|
||||
std::string renderAxis (int, int);
|
||||
|
@ -45,7 +45,7 @@ private:
|
|||
std::string renderHolidays (const std::map <Datetime, std::string>&);
|
||||
std::string renderMonth (const Datetime&, const Datetime&);
|
||||
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 renderWeek (const Datetime&, const Datetime&);
|
||||
std::string renderWeekday (Datetime&, const Color&);
|
||||
|
@ -55,7 +55,7 @@ private:
|
|||
|
||||
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 getHourColor (int) const;
|
||||
|
|
|
@ -86,7 +86,7 @@ Range DatetimeParser::parse_range (const std::string& input)
|
|||
{
|
||||
start = pig.cursor ();
|
||||
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 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)
|
||||
{
|
||||
auto start_date = Datetime (_date);
|
||||
auto end_date = Datetime(start_date.year()+1, 1, 1);
|
||||
return Range { start_date, end_date };
|
||||
return Range {start_date, end_date};
|
||||
}
|
||||
return Range {};
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ Range DatetimeParser::parse_range (const std::string& input)
|
|||
{
|
||||
start = pig.cursor ();
|
||||
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))
|
||||
{
|
||||
return Range { Datetime {_date}, 0 };
|
||||
return Range {Datetime {_date}, 0};
|
||||
}
|
||||
|
||||
if (parse_named_day (pig))
|
||||
{
|
||||
// ::validate and ::resolve are not needed in this case.
|
||||
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))
|
||||
|
@ -171,14 +171,14 @@ Range DatetimeParser::parse_range (const std::string& input)
|
|||
auto month = (begin.month() + 1) % 13 + (begin.month() == 12);
|
||||
auto year = (begin.year() + (begin.month() == 12));
|
||||
auto end = Datetime (year, month, 1);
|
||||
return Range { begin, end };
|
||||
return Range {begin, end};
|
||||
}
|
||||
|
||||
if (parse_named (pig))
|
||||
{
|
||||
// ::validate and ::resolve are not needed in this case.
|
||||
start = pig.cursor ();
|
||||
return Range { Datetime (_date), 0 };
|
||||
return Range {Datetime (_date), 0};
|
||||
}
|
||||
|
||||
throw format ("'{1}' is not a valid range.", input);
|
||||
|
|
|
@ -30,12 +30,15 @@
|
|||
#include <Range.h>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
class Interval : public Range
|
||||
{
|
||||
public:
|
||||
Interval () = default;
|
||||
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;
|
||||
|
||||
|
|
|
@ -41,8 +41,8 @@ int CmdAnnotate (
|
|||
{
|
||||
const bool verbose = rules.getBoolean ("verbose");
|
||||
|
||||
std::set <int> ids = cli.getIds ();
|
||||
std::string annotation = cli.getAnnotation ();
|
||||
auto ids = cli.getIds ();
|
||||
auto annotation = cli.getAnnotation ();
|
||||
|
||||
journal.startTransaction ();
|
||||
flattenDatabase (database, rules);
|
||||
|
|
|
@ -36,9 +36,9 @@
|
|||
#include <iostream>
|
||||
#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 (
|
||||
|
@ -46,16 +46,7 @@ int CmdChartDay (
|
|||
Rules& rules,
|
||||
Database& database)
|
||||
{
|
||||
auto default_hint = rules.get ("reports.range", "day");
|
||||
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);
|
||||
return renderChart ("day", cli, rules, database);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -64,16 +55,7 @@ int CmdChartWeek (
|
|||
Rules& rules,
|
||||
Database& database)
|
||||
{
|
||||
auto default_hint = rules.get ("reports.range", "week");
|
||||
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);
|
||||
return renderChart ("week", cli, rules, database);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -82,32 +64,31 @@ int CmdChartMonth (
|
|||
Rules& rules,
|
||||
Database& database)
|
||||
{
|
||||
auto default_hint = rules.get ("reports.range", "month");
|
||||
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);
|
||||
return renderChart ("month", cli, rules, database);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int renderChart (
|
||||
const CLI& cli,
|
||||
const std::string& type,
|
||||
Interval& filter,
|
||||
const CLI& cli,
|
||||
Rules& rules,
|
||||
Database& database)
|
||||
{
|
||||
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.
|
||||
IntervalFilterAndGroup filtering ({
|
||||
std::make_shared <IntervalFilterAllInRange> ( Range { filter.start, filter.end }),
|
||||
std::make_shared <IntervalFilterAllWithTags> (filter.tags())
|
||||
std::make_shared <IntervalFilterAllInRange> (range),
|
||||
std::make_shared <IntervalFilterAllWithTags> (tags)
|
||||
});
|
||||
|
||||
auto tracked = getTracked (database, rules, filtering);
|
||||
|
@ -118,16 +99,18 @@ int renderChart (
|
|||
{
|
||||
std::cout << "No filtered data found";
|
||||
|
||||
if (filter.is_started ())
|
||||
if (range.is_started ())
|
||||
{
|
||||
std::cout << " in the range " << filter.start.toISOLocalExtended ();
|
||||
if (filter.is_ended ())
|
||||
std::cout << " - " << filter.end.toISOLocalExtended ();
|
||||
std::cout << " in the range " << range.start.toISOLocalExtended ();
|
||||
if (range.is_ended ())
|
||||
{
|
||||
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";
|
||||
|
@ -136,8 +119,8 @@ int renderChart (
|
|||
return 0;
|
||||
}
|
||||
|
||||
const auto exclusions = getAllExclusions (rules, filter);
|
||||
const auto holidays = createHolidayMap (rules, filter);
|
||||
const auto exclusions = getAllExclusions (rules, range);
|
||||
const auto holidays = createHolidayMap (rules, range);
|
||||
|
||||
// Map tags to colors.
|
||||
auto palette = createPalette (rules);
|
||||
|
@ -146,12 +129,16 @@ int renderChart (
|
|||
const auto minutes_per_char = rules.getInteger ("reports." + type + ".cell");
|
||||
|
||||
if (minutes_per_char < 1)
|
||||
{
|
||||
throw format ("The value for 'reports.{1}.cell' must be at least 1.", type);
|
||||
}
|
||||
|
||||
const auto num_lines = rules.getInteger ("reports." + type + ".lines", 1);
|
||||
|
||||
if (num_lines < 1)
|
||||
{
|
||||
throw format ("Invalid value for 'reports.{1}.lines': '{2}'", type, num_lines);
|
||||
}
|
||||
|
||||
ChartConfig configuration {};
|
||||
configuration.reference_datetime = Datetime ();
|
||||
|
@ -177,13 +164,13 @@ int renderChart (
|
|||
|
||||
Chart chart (configuration);
|
||||
|
||||
std::cout << chart.render (filter, tracked, exclusions, holidays);
|
||||
std::cout << chart.render (range, tracked, exclusions, holidays);
|
||||
|
||||
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;
|
||||
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 (), '_', '-');
|
||||
Datetime holiday (date);
|
||||
|
||||
if (holiday >= filter.start && holiday <= filter.end)
|
||||
if (holiday >= range.start && holiday <= range.end)
|
||||
{
|
||||
std::stringstream out;
|
||||
out << " ["
|
||||
|
|
|
@ -44,16 +44,17 @@ int CmdContinue (
|
|||
const bool verbose = rules.getBoolean ("verbose");
|
||||
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.");
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
@ -74,14 +75,14 @@ int CmdContinue (
|
|||
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);
|
||||
|
||||
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
|
||||
|
@ -106,10 +107,10 @@ int CmdContinue (
|
|||
Datetime start_time;
|
||||
Datetime end_time;
|
||||
|
||||
if (filter.is_started ())
|
||||
if (range.is_started ())
|
||||
{
|
||||
start_time = filter.start;
|
||||
end_time = filter.end;
|
||||
start_time = range.start;
|
||||
end_time = range.end;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -39,8 +39,7 @@ int CmdDelete (
|
|||
{
|
||||
const bool verbose = rules.getBoolean ("verbose");
|
||||
|
||||
// Gather IDs.
|
||||
std::set <int> ids = cli.getIds ();
|
||||
auto ids = cli.getIds ();
|
||||
|
||||
if (ids.empty ())
|
||||
{
|
||||
|
@ -53,7 +52,6 @@ int CmdDelete (
|
|||
auto filtering = IntervalFilterAllWithIds (ids);
|
||||
auto intervals = getTracked (database, rules, filtering);
|
||||
|
||||
|
||||
if (intervals.size () != ids.size ())
|
||||
{
|
||||
for (auto& id: ids)
|
||||
|
|
|
@ -38,16 +38,19 @@ int CmdExport (
|
|||
Rules& rules,
|
||||
Database& database)
|
||||
{
|
||||
auto filter = cli.getFilter ();
|
||||
std::set <int> ids = cli.getIds ();
|
||||
auto ids = cli.getIds ();
|
||||
auto range = cli.getRange ();
|
||||
auto tags = cli.getTags ();
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
filtering = std::make_shared <IntervalFilterAllWithIds> (ids);
|
||||
}
|
||||
else
|
||||
|
@ -55,14 +58,15 @@ int CmdExport (
|
|||
filtering = std::make_shared <IntervalFilterAndGroup> (
|
||||
std::vector <std::shared_ptr <IntervalFilter>> (
|
||||
{
|
||||
std::make_shared <IntervalFilterAllInRange> (Range{filter.start, filter.end}),
|
||||
std::make_shared <IntervalFilterAllWithTags> (filter.tags ()),
|
||||
std::make_shared <IntervalFilterAllInRange> (range),
|
||||
std::make_shared <IntervalFilterAllWithTags> (tags),
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
auto intervals = getTracked (database, rules, *filtering);
|
||||
|
||||
std::cout << jsonFromIntervals (intervals);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -40,7 +40,7 @@ int CmdFill (
|
|||
{
|
||||
const bool verbose = rules.getBoolean ("verbose");
|
||||
|
||||
std::set <int> ids = cli.getIds ();
|
||||
auto ids = cli.getIds ();
|
||||
|
||||
if (ids.empty ())
|
||||
{
|
||||
|
@ -48,10 +48,7 @@ int CmdFill (
|
|||
}
|
||||
|
||||
// Load the data.
|
||||
// Note: There is no filter.
|
||||
Interval filter;
|
||||
|
||||
auto filtering = IntervalFilterAllInRange ({ 0, 0 });
|
||||
auto filtering = IntervalFilterAllInRange ({0, 0});
|
||||
auto tracked = getTracked (database, rules, filtering);
|
||||
|
||||
journal.startTransaction ();
|
||||
|
@ -60,7 +57,9 @@ int CmdFill (
|
|||
for (auto& id : ids)
|
||||
{
|
||||
if (id > static_cast <int> (tracked.size ()))
|
||||
{
|
||||
throw format ("ID '@{1}' does not correspond to any tracking.", id);
|
||||
}
|
||||
|
||||
Interval from = tracked[tracked.size () - id];
|
||||
std::cout << "# from " << from.dump () << "\n";
|
||||
|
|
|
@ -45,16 +45,22 @@ int CmdGaps (
|
|||
Range 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?
|
||||
bool blank = cli.getHint ("blank", false);
|
||||
|
||||
std::vector <Range> untracked;
|
||||
if (blank)
|
||||
untracked = subtractRanges ({filter}, getAllExclusions (rules, filter));
|
||||
{
|
||||
untracked = subtractRanges ({range}, getAllExclusions (rules, range));
|
||||
}
|
||||
else
|
||||
{
|
||||
untracked = getUntracked (database, rules, filter);
|
||||
}
|
||||
|
||||
Table table;
|
||||
table.width (1024);
|
||||
|
@ -70,7 +76,7 @@ int CmdGaps (
|
|||
// Each day is rendered separately.
|
||||
time_t grand_total = 0;
|
||||
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);
|
||||
time_t daily_total = 0;
|
||||
|
|
|
@ -37,9 +37,10 @@ int CmdGet (
|
|||
Rules& rules,
|
||||
Database& database)
|
||||
{
|
||||
auto references = cli.getDomReferences ();
|
||||
auto filter = Interval {cli.getRange (), cli.getTags()};
|
||||
|
||||
std::vector <std::string> results;
|
||||
std::vector <std::string> references = cli.getDomReferences ();
|
||||
Interval filter = cli.getFilter ();
|
||||
|
||||
for (auto& reference : references)
|
||||
{
|
||||
|
|
|
@ -41,7 +41,7 @@ int CmdJoin (
|
|||
const bool verbose = rules.getBoolean ("verbose");
|
||||
|
||||
// Gather IDs and TAGs.
|
||||
std::set <int> ids = cli.getIds ();
|
||||
auto ids = cli.getIds ();
|
||||
|
||||
// Only 2 IDs allowed in a join.
|
||||
if (ids.size () != 2)
|
||||
|
|
|
@ -40,15 +40,14 @@ int CmdLengthen (
|
|||
{
|
||||
const bool verbose = rules.getBoolean ("verbose");
|
||||
|
||||
// Gather IDs and TAGs.
|
||||
std::set <int> ids = cli.getIds ();
|
||||
auto ids = cli.getIds ();
|
||||
|
||||
if (ids.empty ())
|
||||
{
|
||||
throw std::string ("IDs must be specified. See 'timew help lengthen'.");
|
||||
}
|
||||
|
||||
Duration dur = cli.getDuration ();
|
||||
auto dur = cli.getDuration ();
|
||||
|
||||
journal.startTransaction ();
|
||||
|
||||
|
|
|
@ -39,10 +39,9 @@ int CmdModify (
|
|||
{
|
||||
const bool verbose = rules.getBoolean ("verbose");
|
||||
|
||||
auto filter = cli.getFilter ();
|
||||
std::set <int> ids = cli.getIds ();
|
||||
std::vector <std::string> words = cli.getWords ();
|
||||
enum { MODIFY_START, MODIFY_END } op = MODIFY_START;
|
||||
auto words = cli.getWords ();
|
||||
|
||||
enum {MODIFY_START, MODIFY_END} op = MODIFY_START;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
auto ids = cli.getIds ();
|
||||
|
||||
if (ids.empty ())
|
||||
{
|
||||
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'.");
|
||||
}
|
||||
|
||||
auto range = cli.getRange ({0, 0});
|
||||
|
||||
int id = *ids.begin();
|
||||
|
||||
flattenDatabase (database, rules);
|
||||
|
@ -84,7 +87,7 @@ int CmdModify (
|
|||
}
|
||||
|
||||
assert (intervals.size () == 1);
|
||||
if (! filter.is_started ())
|
||||
if (! range.is_started ())
|
||||
{
|
||||
throw std::string ("No updated time specified. See 'timew help modify'.");
|
||||
}
|
||||
|
@ -94,7 +97,7 @@ int CmdModify (
|
|||
switch (op)
|
||||
{
|
||||
case MODIFY_START:
|
||||
modified.start = filter.start;
|
||||
modified.start = range.start;
|
||||
break;
|
||||
|
||||
case MODIFY_END:
|
||||
|
@ -102,7 +105,7 @@ int CmdModify (
|
|||
{
|
||||
throw format ("Cannot modify end of open interval @{1}.", id);
|
||||
}
|
||||
modified.end = filter.start;
|
||||
modified.end = range.start;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -134,24 +134,28 @@ int CmdReport (
|
|||
expandIntervalHint (":" + report_hint, default_range);
|
||||
|
||||
// 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 ({
|
||||
std::make_shared <IntervalFilterAllInRange> ( Range { filter.start, filter.end }),
|
||||
std::make_shared <IntervalFilterAllWithTags> (filter.tags ())
|
||||
std::make_shared <IntervalFilterAllInRange> (range),
|
||||
std::make_shared <IntervalFilterAllWithTags> (tags)
|
||||
});
|
||||
|
||||
auto tracked = getTracked (database, rules, filtering);
|
||||
|
||||
// Compose Header info.
|
||||
rules.set ("temp.report.start", filter.is_started () ? filter.start.toISO () : "");
|
||||
rules.set ("temp.report.end", filter.is_ended () ? filter.end.toISO () : "");
|
||||
rules.set ("temp.report.tags", joinQuotedIfNeeded (",", filter.tags ()));
|
||||
rules.set ("temp.report.start", range.is_started () ? range.start.toISO () : "");
|
||||
rules.set ("temp.report.end", range.is_ended () ? range.end.toISO () : "");
|
||||
rules.set ("temp.report.tags", joinQuotedIfNeeded (",", tags));
|
||||
rules.set ("temp.version", VERSION);
|
||||
|
||||
std::stringstream header;
|
||||
|
||||
for (auto& name : rules.all ())
|
||||
{
|
||||
header << name << ": " << rules.get (name) << '\n';
|
||||
}
|
||||
|
||||
// Get the data.
|
||||
auto input = header.str ()
|
||||
|
@ -168,7 +172,9 @@ int CmdReport (
|
|||
|
||||
// Display the output.
|
||||
for (auto& line : output)
|
||||
{
|
||||
std::cout << line << '\n';
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -38,13 +38,14 @@ int CmdStart (
|
|||
const bool verbose = rules.getBoolean ("verbose");
|
||||
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.");
|
||||
}
|
||||
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. "
|
||||
"Perhaps you want the track command?");
|
||||
|
@ -58,6 +59,8 @@ int CmdStart (
|
|||
}
|
||||
|
||||
journal.startTransaction ();
|
||||
auto interval = Interval {range, tags};
|
||||
|
||||
if (validate (cli, rules, database, interval))
|
||||
{
|
||||
database.addInterval (interval, verbose);
|
||||
|
|
|
@ -50,7 +50,6 @@ int CmdStop (
|
|||
const bool verbose = rules.getBoolean ("verbose");
|
||||
const Datetime now {};
|
||||
|
||||
auto filter = cli.getFilter ({ now, 0 });
|
||||
// Load the most recent interval.
|
||||
auto latest = getLatestInterval (database);
|
||||
|
||||
|
@ -67,30 +66,34 @@ int CmdStop (
|
|||
"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.");
|
||||
}
|
||||
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.");
|
||||
}
|
||||
|
||||
auto tags = cli.getTags ();
|
||||
|
||||
std::set <std::string> diff = {};
|
||||
|
||||
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 (),
|
||||
std::inserter(diff, 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 (),
|
||||
filter.tags ().begin (), filter.tags ().end (),
|
||||
tags.begin (), tags.end (),
|
||||
std::inserter(diff, diff.begin()));
|
||||
}
|
||||
|
||||
|
@ -98,8 +101,8 @@ int CmdStop (
|
|||
|
||||
if (diff.empty ())
|
||||
{
|
||||
Interval modified { latest };
|
||||
modified.end = filter.start;
|
||||
Interval modified {latest};
|
||||
modified.end = range.start;
|
||||
|
||||
database.deleteInterval (latest);
|
||||
validate (cli, rules, database, modified);
|
||||
|
@ -116,7 +119,7 @@ int CmdStop (
|
|||
}
|
||||
else
|
||||
{
|
||||
Interval next { filter.start, 0 };
|
||||
Interval next {range.start, 0};
|
||||
for (auto& tag : diff)
|
||||
{
|
||||
next.tag (tag);
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
#include <utf8.h>
|
||||
|
||||
// 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>&);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -53,12 +53,13 @@ int CmdSummary (
|
|||
Range 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.
|
||||
IntervalFilterAndGroup filtering ({
|
||||
std::make_shared <IntervalFilterAllInRange> ( Range { filter.start, filter.end }),
|
||||
std::make_shared <IntervalFilterAllWithTags> (filter.tags())
|
||||
std::make_shared <IntervalFilterAllInRange> (range),
|
||||
std::make_shared <IntervalFilterAllWithTags> (tags)
|
||||
});
|
||||
|
||||
auto tracked = getTracked (database, rules, filtering);
|
||||
|
@ -69,16 +70,18 @@ int CmdSummary (
|
|||
{
|
||||
std::cout << "No filtered data found";
|
||||
|
||||
if (filter.is_started ())
|
||||
if (range.is_started ())
|
||||
{
|
||||
std::cout << " in the range " << filter.start.toISOLocalExtended ();
|
||||
if (filter.is_ended ())
|
||||
std::cout << " - " << filter.end.toISOLocalExtended ();
|
||||
std::cout << " in the range " << range.start.toISOLocalExtended ();
|
||||
if (range.is_ended ())
|
||||
{
|
||||
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";
|
||||
|
@ -159,8 +162,8 @@ int CmdSummary (
|
|||
time_t grand_total = 0;
|
||||
Datetime previous;
|
||||
|
||||
auto days_start = filter.is_started() ? filter.start : tracked.front ().start;
|
||||
auto days_end = filter.is_ended() ? filter.end : tracked.back ().end;
|
||||
auto days_start = range.is_started() ? range.start : tracked.front ().start;
|
||||
auto days_end = range.is_ended() ? range.end : tracked.back ().end;
|
||||
|
||||
const auto now = Datetime ();
|
||||
|
||||
|
@ -221,8 +224,8 @@ int CmdSummary (
|
|||
|
||||
if (show_tags)
|
||||
{
|
||||
std::string tags = join (", ", track.tags ());
|
||||
table.set (row, tags_col_index, tags, summaryIntervalColor (rules, track.tags ()));
|
||||
std::string tags_string = join (", ", track.tags ());
|
||||
table.set (row, tags_col_index, tags_string, summaryIntervalColor (rules, track.tags ()));
|
||||
}
|
||||
|
||||
if (show_annotations)
|
||||
|
@ -260,7 +263,7 @@ int CmdSummary (
|
|||
|
||||
std::cout << '\n'
|
||||
<< table.render ()
|
||||
<< (show_holidays ? renderHolidays (createHolidayMap (rules, filter)) : "")
|
||||
<< (show_holidays ? renderHolidays (createHolidayMap (rules, range)) : "")
|
||||
<< '\n';
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -43,7 +43,7 @@ int CmdTag (
|
|||
|
||||
// Gather IDs and TAGs.
|
||||
std::set <int> ids = cli.getIds ();
|
||||
std::vector<std::string> tags = cli.getTags ();
|
||||
std::set<std::string> tags = cli.getTags ();
|
||||
|
||||
if (tags.empty ())
|
||||
{
|
||||
|
|
|
@ -42,15 +42,14 @@ int CmdTags (
|
|||
{
|
||||
const bool verbose = rules.getBoolean ("verbose");
|
||||
|
||||
// Create a filter, with no default range.
|
||||
auto filter = cli.getFilter ();
|
||||
IntervalFilterAndGroup filtering ({
|
||||
std::make_shared <IntervalFilterAllInRange> ( Range { filter.start, filter.end }),
|
||||
std::make_shared <IntervalFilterAllWithTags> (filter.tags ())
|
||||
std::make_shared <IntervalFilterAllInRange> (cli.getRange ()),
|
||||
std::make_shared <IntervalFilterAllWithTags> (cli.getTags ())
|
||||
});
|
||||
|
||||
// Generate a unique, ordered list of tags.
|
||||
std::set <std::string> tags;
|
||||
|
||||
for (const auto& interval : getTracked (database, rules, filtering))
|
||||
for (auto& tag : interval.tags ())
|
||||
tags.insert (tag);
|
||||
|
|
|
@ -37,8 +37,6 @@ int CmdTrack (
|
|||
{
|
||||
const bool verbose = rules.getBoolean ("verbose");
|
||||
|
||||
auto filter = cli.getFilter ();
|
||||
|
||||
// We expect no ids
|
||||
if (! cli.getIds ().empty ())
|
||||
{
|
||||
|
@ -46,23 +44,32 @@ int CmdTrack (
|
|||
"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
|
||||
// the 'track' command behave like 'start', so delegate to CmdStart.
|
||||
if (! filter.is_started () ||
|
||||
! filter.is_ended ())
|
||||
if (! range.is_started () || ! range.is_ended ())
|
||||
{
|
||||
return CmdStart (cli, rules, database, journal);
|
||||
}
|
||||
|
||||
auto tags = cli.getTags ();
|
||||
|
||||
journal.startTransaction ();
|
||||
|
||||
auto filter = Interval {range, tags};
|
||||
|
||||
// Validation must occur before flattening.
|
||||
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);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
std::cout << intervalSummarize (rules, interval);
|
||||
}
|
||||
}
|
||||
|
||||
journal.endTransaction ();
|
||||
|
|
|
@ -43,7 +43,7 @@ int CmdUntag (
|
|||
|
||||
// Gather IDs and TAGs.
|
||||
std::set <int> ids = cli.getIds ();
|
||||
std::vector<std::string> tags = cli.getTags ();
|
||||
std::set<std::string> tags = cli.getTags ();
|
||||
|
||||
if (tags.empty ())
|
||||
{
|
||||
|
|
|
@ -119,8 +119,8 @@ bool domGet (
|
|||
else if (pig.skipLiteral ("tracked."))
|
||||
{
|
||||
IntervalFilterAndGroup filtering ({
|
||||
std::make_shared <IntervalFilterAllInRange> ( Range { filter.start, filter.end }),
|
||||
std::make_shared <IntervalFilterAllWithTags> (filter.tags())
|
||||
std::make_shared <IntervalFilterAllInRange> (Range {filter.start, filter.end}),
|
||||
std::make_shared <IntervalFilterAllWithTags> (filter.tags ())
|
||||
});
|
||||
|
||||
auto tracked = getTracked (database, rules, filtering);
|
||||
|
|
|
@ -124,7 +124,7 @@ class TestTag(TestCase):
|
|||
|
||||
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()
|
||||
self.assertOpenInterval(j[0], expectedTags=["bar", "foo"])
|
||||
|
@ -138,7 +138,7 @@ class TestTag(TestCase):
|
|||
|
||||
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()
|
||||
self.assertClosedInterval(j[0], expectedTags=["bar", "foo"])
|
||||
|
@ -171,7 +171,7 @@ class TestTag(TestCase):
|
|||
|
||||
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()
|
||||
self.assertClosedInterval(j[0], expectedTags=["bar", "foo", "one"])
|
||||
|
|
|
@ -124,7 +124,7 @@ class TestUntag(TestCase):
|
|||
|
||||
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()
|
||||
self.assertOpenInterval(j[0], expectedTags=["baz"])
|
||||
|
@ -138,7 +138,7 @@ class TestUntag(TestCase):
|
|||
|
||||
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()
|
||||
self.assertClosedInterval(j[0], expectedTags=["baz"])
|
||||
|
@ -171,7 +171,7 @@ class TestUntag(TestCase):
|
|||
|
||||
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()
|
||||
self.assertClosedInterval(j[0], expectedTags=["one"])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue