From a7244a999ee4e044908b75bf706c5e74723fe86a Mon Sep 17 00:00:00 2001 From: Federico Hernandez Date: Fri, 22 Jan 2010 01:42:32 +0100 Subject: [PATCH] Holidays - added displaying of holidays in 'task cal' via calendar.holidays - the legend in the calendar can now be turned off - weeknumbers in the calendar can now be color-coded --- doc/man/taskrc.5 | 62 ++++++++++++++++-- src/Config.cpp | 12 ++-- src/command.cpp | 19 +++--- src/report.cpp | 165 ++++++++++++++++++++++++++++++++++++----------- src/tests/cal.t | 62 +++++++++++++----- 5 files changed, 247 insertions(+), 73 deletions(-) diff --git a/doc/man/taskrc.5 b/doc/man/taskrc.5 index 5a8445a03..853883881 100644 --- a/doc/man/taskrc.5 +++ b/doc/man/taskrc.5 @@ -167,6 +167,7 @@ according to dateformat. The default value is: m/d/Y. The string should contain the characters +.RS .RS m minimal-digit month, for example 1 or 12 .br @@ -190,10 +191,14 @@ B long name of month, for example January or August .br V weeknumber, for example 03 or 37 .RE +.RE +.RS The string may also contain other characters to act as spacers, or formatting. Examples for other values of dateformat: +.RE +.RS .RS .br d/m/Y would use for input and output 24/7/2009 @@ -202,9 +207,13 @@ yMD would use for input and output 090724 .br M-D-Y would use for input and output 07-24-2009 .RE +.RE +.RS Examples for other values of reportdateformat: +.RE +.RS .RS .br a D b Y (V) would do an output as "Fri 24 Jul 2009 (30)" @@ -213,6 +222,7 @@ A, B D, Y would do an output as "Friday, July 24, 2009" .br vV a Y-M-D would do an output as "v30 Fri 2009-07.24" .RE +.RE .TP .B weekstart=Sunday @@ -228,14 +238,48 @@ The week number is dependent on the day a week starts. This is the number of days into the future that define when a task is considered due, and is colored accordingly. Defaults to 7. -.TP calendar.details=no -If set to yes running "task calendar" will display the details of tasks with due dates -that fall into the calendar period. +.TP +.B calendar.details=sparse +If set to full running "task calendar" will display the details of tasks with due dates +that fall into the calendar period. The corresponding days will be color-coded in the +calendar. If set to sparse only the corresponding days will be color coded and no details +will be displayed. The displaying of due dates with details is turned off by setting the +variable to none. -.TP calendar.details.report=list +.TP +.B calendar.details.report=list The report to run when displaying the details of tasks with due date when running the "task calendar" command. +.TP +.B calendar.holidays=full +If set to full running "task calendar" will display holidays in the calendar by color-coding +the corresponding days. A detailed list with the dates and names of the holidays is also shown. +If set to sparse only the days are color-coded and no details on the holidays will be +displayed. The displaying of holidays is turned off by setting the variable to none. + +.TP +.B Holidays +Holidays are entered either directly in the .taskrc file or via an include file that is specified +in .taskrc. For each holiday the name and the date is required to be given: + +.RS +.RS +.br +holiday.towel.name=Day of the towel +.br +holiday.towel.date=20100525 +.br +holiday.sysadmin.name=System Administrator Appreciation Day +.br +holiday.sysadmin.date=20100730 +.RE +.RE + +.RS +Dates are to be entered according to the setting in the dateformat variable. +.RE + .TP .B monthsperline=2 Determines how many months the "task calendar" command renders across the screen. @@ -351,8 +395,16 @@ Color of days with due tasks in calendar. Color of days with overdue tasks in calendar. .TP -.B color.calendar.weekend=black on white +.B color.calendar.weekend=bright white on black Color of weekend days in calendar. + +.TP +.B color.calendar.holiday=black on bright yellow +Color of holidays in calendar. + +.TP +.B color.calendar.weeknumber=black on white +Color of weeknumbers in calendar. .RE .SS SHADOW FILE diff --git a/src/Config.cpp b/src/Config.cpp index 23f847d16..da4d5bac7 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -70,8 +70,10 @@ std::string Config::defaults = "weekstart=Sunday # Sunday or Monday only\n" "displayweeknumber=yes # Show week numbers on calendar\n" "due=7 # Task is considered due in 7 days\n" - "#calendar.details=yes # Calendar shows information for tasks w/due dates\n" + "calendar.legend=yes # Display the legend on calendar\n" + "#calendar.details=full # Calendar shows information for tasks w/due dates\n" "#calendar.details.report=list # Report to use when showing task information in cal\n" + "#calendar.holidays=none # Show public holidays on calendar\n" "#monthsperline=3 # Number of calendar months on a line\n" // TODO "\n" "# Color controls.\n" @@ -94,12 +96,10 @@ std::string Config::defaults = "color.calendar.today=black on cyan # Color of today in calendar\n" "color.calendar.due=black on green # Color of days with due tasks in calendar\n" "color.calendar.overdue=black on red # Color of days with overdue tasks in calendar\n" - "color.calendar.weekend=black on white # Color of weekend days in calendar\n" + "color.calendar.weekend=bright white on black # Color of weekend days in calendar\n" + "color.calendar.holiday=black on bright yellow # Color of public holidays in calendar\n" + "color.calendar.weeknumber=black on white # Color of the weeknumbers in calendar\n" "#color.debug=magenta # Color of diagnostic output\n" - "color.pri.H=bold # Color of priority:H tasks\n" - "color.history.add=on red # Color of added tasks in the history reports\n" - "color.history.delete=on yellow # Color of deleted tasks in the history reports\n" - "color.history.done=on green # Color of completed tasks in the history reports\n" "\n" "# Shadow file support\n" "#shadow.file=/tmp/shadow.txt # Location of shadow file\n" diff --git a/src/command.cpp b/src/command.cpp index c6cd73e5b..72727e5b5 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -649,15 +649,15 @@ int handleConfig (std::string &outs) // These are the regular configuration variables. // Note that there is a leading and trailing space, to make searching easier. std::string recognized = - " annotations blanklines bulk calendar.details calendar.details.report color " - "color.active color.due color.overdue color.pri.H color.pri.L color.pri.M color.pri.none " - "color.recurring color.tagged color.footnote color.header color.debug color.alternate " - "color.calendar.today color.calendar.due color.calendar.overdue color.calendar.weekend " - "confirmation curses data.location dateformat reportdateformat debug default.command " - "default.priority default.project defaultwidth due locale displayweeknumber " - "echo.command locking monthsperline nag next project shadow.command shadow.file " - "shadow.notify weekstart editor import.synonym.id import.synonym.uuid " - "complete.all.projects complete.all.tags " + " annotations blanklines bulk calendar.details calendar.details.report calendar.holidays " + "calendar.legend color color.active color.due color.overdue color.pri.H color.pri.L " + "color.pri.M color.pri.none color.recurring color.tagged color.footnote color.header " + "color.debug color.alternate color.calendar.today color.calendar.due color.calendar.overdue " + "color.calendar.weekend color.calendar.holiday color.calendar.weeknumber confirmation " + "curses data.location dateformat reportdateformat debug default.command default.priority " + "default.project defaultwidth due locale displayweeknumber echo.command fontunderline " + "locking monthsperline nag next project shadow.command shadow.file shadow.notify weekstart " + "editor import.synonym.id import.synonym.uuid complete.all.projects complete.all.tags " #ifdef FEATURE_SHELL "shell.prompt " #endif @@ -684,6 +684,7 @@ int handleConfig (std::string &outs) if (i->substr (0, 14) != "color.keyword." && i->substr (0, 14) != "color.project." && i->substr (0, 10) != "color.tag." && + i->substr (0, 8) != "holiday." && i->substr (0, 7) != "report." && i->substr (0, 6) != "alias.") { diff --git a/src/report.cpp b/src/report.cpp index 4bb7d16c5..b97b026ca 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -1416,6 +1416,13 @@ std::string renderMonths ( int row = 0; + Color color_today (context.config.get ("color.calendar.today")); + Color color_due (context.config.get ("color.calendar.due")); + Color color_overdue (context.config.get ("color.calendar.overdue")); + Color color_weekend (context.config.get ("color.calendar.weekend")); + Color color_holiday (context.config.get ("color.calendar.holiday")); + Color color_weeknumber (context.config.get ("color.calendar.weeknumber")); + // Loop through months to be added on this line. for (int mpl = 0; mpl < monthsPerLine ; mpl++) { @@ -1431,7 +1438,11 @@ std::string renderMonths ( int woy = temp.weekOfYear (weekStart); if (context.config.getBoolean ("displayweeknumber")) + { table.addCell (row, (8 * mpl), woy); + if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) + table.setCellColor (row, (8 * mpl), color_weeknumber); + } // Calculate column id. int thisCol = dow + // 0 = Sunday @@ -1443,35 +1454,52 @@ std::string renderMonths ( table.addCell (row, thisCol, d); - Color color_today (context.config.get ("color.calendar.today")); - Color color_due (context.config.get ("color.calendar.due")); - Color color_overdue (context.config.get ("color.calendar.overdue")); - Color color_weekend (context.config.get ("color.calendar.weekend")); - if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) { + + // colorize weekends if (dow == 0 || dow == 6) table.setCellColor (row, thisCol, color_weekend); + // colorize holidays + if (context.config.get ("calendar.holidays") != "none") + { + std::vector holidays; + context.config.all (holidays); + foreach (hol, holidays) + if (hol->substr (0, 8) == "holiday.") + if (hol->substr (hol->size () - 4) == "date") + { + std::string value = context.config.get (*hol); + Date holDate (value.c_str (), context.config.get ("dateformat")); + if (holDate.day () == d && + holDate.month () == months[mpl] && + holDate.year () == years[mpl]) + table.setCellColor (row, thisCol, color_holiday); + } + } + + // colorize today if (today.day () == d && today.month () == months.at (mpl) && today.year () == years.at (mpl)) table.setCellColor (row, thisCol, color_today); - } - foreach (task, all) - { - if (task->getStatus () == Task::pending && - task->has ("due")) - { - Date due (atoi (task->get ("due").c_str ())); + // colorize due tasks + if (context.config.get ("calendar.details") != "none") + foreach (task, all) + { + if (task->getStatus () == Task::pending && + task->has ("due")) + { + Date due (atoi (task->get ("due").c_str ())); - if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && - due.day () == d && - due.month () == months[mpl] && - due.year () == years[mpl]) - table.setCellColor (row, thisCol, (due < today ? color_overdue : color_due)); - } + if (due.day () == d && + due.month () == months[mpl] && + due.year () == years[mpl]) + table.setCellColor (row, thisCol, (due < today ? color_overdue : color_due)); + } + } } // Check for end of week, and... @@ -1652,12 +1680,15 @@ int handleReportCalendar (std::string &outs) } } - Color color_today (context.config.get ("color.calendar.today")); - Color color_due (context.config.get ("color.calendar.due")); - Color color_overdue (context.config.get ("color.calendar.overdue")); - Color color_weekend (context.config.get ("color.calendar.weekend")); + Color color_today (context.config.get ("color.calendar.today")); + Color color_due (context.config.get ("color.calendar.due")); + Color color_overdue (context.config.get ("color.calendar.overdue")); + Color color_weekend (context.config.get ("color.calendar.weekend")); + Color color_holiday (context.config.get ("color.calendar.holiday")); + Color color_weeknumber (context.config.get ("color.calendar.weeknumber")); - if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) + if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && + context.config.getBoolean ("calendar.legend")) out << "Legend: " << color_today.colorize ("today") << ", " @@ -1666,11 +1697,15 @@ int handleReportCalendar (std::string &outs) << color_overdue.colorize ("overdue") << ", " << color_weekend.colorize ("weekend") + << ", " + << color_holiday.colorize ("holiday") + << ", " + << color_weeknumber.colorize ("weeknumber") << "." << optionalBlankLine () << std::endl; - if (context.config.getBoolean ("calendar.details")) + if (context.config.get ("calendar.details") == "full" || context.config.get ("calendar.holidays") == "full") { --details_mFrom; if (details_mFrom == 0) @@ -1693,22 +1728,80 @@ int handleReportCalendar (std::string &outs) Date date_before (mTo, 1, yTo); std::string before = date_before.toString (context.config.get ("dateformat")); - std::string report = context.config.get ("calendar.details.report"); - std::string report_filter = context.config.get ("report." + report + ".filter"); + // Table with due date information + if (context.config.get ("calendar.details") == "full") + { + std::string report = context.config.get ("calendar.details.report"); + std::string report_filter = context.config.get ("report." + report + ".filter"); - report_filter += " due.after:" + after + " due.before:" + before; - context.config.set ("report." + report + ".filter", report_filter); + report_filter += " due.after:" + after + " due.before:" + before; + context.config.set ("report." + report + ".filter", report_filter); - // Display all due task in the report colorized not only the imminet ones - context.config.set ("due", 0); + // Display all due task in the report colorized not only the imminet ones + context.config.set ("due", 0); - context.args.clear (); - context.filter.clear (); - context.sequence.clear (); + context.args.clear (); + context.filter.clear (); + context.sequence.clear (); - std::string output; - handleCustomReport (report, output); - out << output; + std::string output; + handleCustomReport (report, output); + out << output; + } + + // Table with holiday information + if (context.config.get ("calendar.holidays") == "full") + { + std::vector holidays; + context.config.all (holidays); + + Table holTable; + holTable.setTableWidth (context.getWidth ()); + holTable.addColumn ("Date"); + holTable.addColumn ("Holiday"); + + if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && + context.config.getBoolean ("fontunderline")) + { + holTable.setColumnUnderline (0); + holTable.setColumnUnderline (1); + } + else + holTable.setTableDashedUnderline (); + + holTable.setColumnWidth (0, Table::minimum); + holTable.setColumnWidth (1, Table::flexible); + + holTable.setColumnJustification (0, Table::left); + holTable.setColumnJustification (1, Table::left); + + foreach (hol, holidays) + if (hol->substr (0, 8) == "holiday.") + if (hol->substr (hol->size () - 4) == "name") + { + std::string holName = context.config.get ("holiday." + hol->substr (8, hol->size () - 13) + ".name"); + std::string holDate = context.config.get ("holiday." + hol->substr (8, hol->size () - 13) + ".date"); + Date hDate (holDate.c_str (), context.config.get ("dateformat")); + + if (date_after < hDate && hDate < date_before) + { + std::string format = context.config.get ("report." + + context.config.get ("calendar.details.report") + + ".dateformat"); + if (format == "") + format = context.config.get ("reportdateformat"); + if (format == "") + format = context.config.get ("dateformat"); + + int row = holTable.addRow (); + holTable.addCell (row, 0, hDate.toString (format)); + holTable.addCell (row, 1, holName); + } + } + out << optionalBlankLine () + << holTable.render () + << std::endl; + } } outs = out.str (); diff --git a/src/tests/cal.t b/src/tests/cal.t index c2b7d4e60..d547ad21e 100755 --- a/src/tests/cal.t +++ b/src/tests/cal.t @@ -30,7 +30,7 @@ use strict; use warnings; -use Test::More tests => 60; +use Test::More tests => 75; # Create the rc file. if (open my $fh, '>', 'cal.rc') @@ -65,9 +65,9 @@ $output = qx{../task rc:cal.rc add zero}; unlike ($output, qr/\[41m\d+/, 'No overdue tasks are present'); unlike ($output, qr/\[43m\d+/, 'No due tasks are present'); $output = qx{../task rc:cal.rc rc.weekstart:Sunday cal}; -like ($output, qr/Su Mo Tu/, 'Week starts on Sunday'); +like ($output, qr/Su Mo Tu/, 'Week starts on Sunday'); $output = qx{../task rc:cal.rc rc.weekstart:Monday cal}; -like ($output, qr/Fr Sa Su/, 'Week starts on Monday'); +like ($output, qr/Fr Sa Su/, 'Week starts on Monday'); $output = qx{../task rc:cal.rc cal y}; like ($output, qr/$month\w+?\s+?$year/, 'Current month and year are displayed'); if ( $month eq "Jan") @@ -85,22 +85,23 @@ unlike ($output, qr/$month\w+?\s+?$nextyear/, 'Current month and year ahead qx{../task rc:cal.rc add due:20190515 one}; qx{../task rc:cal.rc add due:20200123 two}; $output = qx{../task rc:cal.rc rc._forcecolor:on cal due}; -unlike ($output, qr/April 2019/, 'April 2019 is not displayed'); -like ($output, qr/May 2019/, 'May 2019 is displayed'); -unlike ($output, qr/January 2020/, 'January 2020 is not displayed'); +unlike ($output, qr/April 2019/, 'April 2019 is not displayed'); +like ($output, qr/May 2019/, 'May 2019 is displayed'); +unlike ($output, qr/January 2020/, 'January 2020 is not displayed'); like ($output, qr/30;42m15/, 'Task 1 is color-coded due'); $output = qx{../task rc:cal.rc rc._forcecolor:on cal due y}; like ($output, qr/30;42m23/, 'Task 2 is color-coded due'); -like ($output, qr/April 2020/, 'April 2020 is displayed'); -unlike ($output, qr/May 2020/, 'May 2020 is not displayed'); +like ($output, qr/April 2020/, 'April 2020 is displayed'); +unlike ($output, qr/May 2020/, 'May 2020 is not displayed'); qx{../task rc:cal.rc ls}; qx{../task rc:cal.rc del 1-3}; qx{../task rc:cal.rc add due:20080408 three}; $output = qx{../task rc:cal.rc rc._forcecolor:on cal due}; -like ($output, qr/April 2008/, 'April 2008 is displayed'); -like ($output, qr/41m 8/, 'Task 3 is color-coded overdue'); -like ($output, qr/30;47m19/, 'Saturday April 19, 2008 is color-coded'); -like ($output, qr/30;47m20/, 'Sunday April 20, 2008 is color-coded'); +like ($output, qr/April 2008/, 'April 2008 is displayed'); +like ($output, qr/41m 8/, 'Task 3 is color-coded overdue'); +like ($output, qr/37;100m19/, 'Saturday April 19, 2008 is color-coded'); +like ($output, qr/37;100m20/, 'Sunday April 20, 2008 is color-coded'); +like ($output, qr/30;47m 1/, 'Weeknumbers are color-coded'); # task cal 2016 $output = qx{../task rc:cal.rc rc.weekstart:Monday cal 2016}; @@ -120,9 +121,9 @@ unlike ($output, qr/53/, 'Weeknumbers are not displayed'); # task cal 4 2010 $output = qx{../task rc:cal.rc rc.monthsperline:1 cal 4 2010}; -unlike ($output, qr/March 2010/, 'March 2010 is not displayed'); -like ($output, qr/April 2010/, 'April 2010 is displayed'); -unlike ($output, qr/May 2010/, 'May 2010 is not displayed'); +unlike ($output, qr/March 2010/, 'March 2010 is not displayed'); +like ($output, qr/April 2010/, 'April 2010 is displayed'); +unlike ($output, qr/May 2010/, 'May 2010 is not displayed'); # Cleanup. unlink 'pending.data'; @@ -137,10 +138,17 @@ if (open my $fh, '>', 'details.rc') { print $fh "data.location=.\n", "dateformat=YMD\n", - "calendar.details=yes\n", + "calendar.details=full\n", "calendar.details.report=list\n", + "calendar.holidays=full\n", "color=on\n", - "confirmation=no\n"; + "confirmation=no\n", + "holiday.AAAA.name=AAAA\n", + "holiday.AAAA.date=20150101\n", + "holiday.BBBBBB.name=BBBBBB\n", + "holiday.BBBBBB.date=20150115\n", + "holiday.åäö.name=åäö\n", + "holiday.åäö.date=20150125\n"; close $fh; ok (-r 'details.rc', 'Created details.rc'); } @@ -155,6 +163,9 @@ qx{../task rc:details.rc add due:20141231 six}; qx{../task rc:details.rc add due:20160101 seven}; qx{../task rc:details.rc add due:20081231 eight}; +$output = qx{../task rc:details.rc rc.calendar.legend:no cal}; +unlike ($output, qr/Legend:/, 'Legend is not displayed'); + $output = qx{../task rc:details.rc cal rc.monthsperline:3 1 2015}; like ($output, qr/January 2015/, 'January 2015 is displayed'); like ($output, qr/20150105/, 'Due date 20150105 is displayed'); @@ -194,6 +205,23 @@ like ($output, qr/$month\w+?\s+?$year/, 'Current month and year are displayed' like ($output, qr/$duedate/, 'Due date on current day is displayed'); like ($output, qr/1 task/, '1 due task is displayed'); +$output = qx{../task rc:details.rc cal rc.monthsperline:1 1 2015}; +like ($output, qr/Date/, 'Word Date is displayed'); +like ($output, qr/Holiday/, 'Word Holiday is displayed'); +like ($output, qr/20150101/, 'Holiday 20150101 is displayed'); +like ($output, qr/20150115/, 'Holiday 20150115 is displayed'); +like ($output, qr/20150125/, 'Holiday 20150125 is displayed'); +like ($output, qr/AAAA/, 'Holiday name AAAA is displayed'); +like ($output, qr/BBBBBB/, 'Holiday name BBBBBB is displayed'); +like ($output, qr/åäö/, 'Holiday name åäö is displayed'); + +$output = qx{../task rc:details.rc cal rc._forcecolor:on rc.monthsperline:1 rc.calendar.details:sparse rc.calendar.holidays:sparse 1 2015}; +unlike ($output, qr/Date/, 'Word Date is not displayed'); +unlike ($output, qr/Holiday/, 'Word Holiday is not displayed'); +like ($output, qr/30;103m 1/, 'Holiday AAAA is color-coded'); +like ($output, qr/30;103m15/, 'Holiday BBBBBB is color-coded'); +like ($output, qr/30;103m25/, 'Holiday åäö is color-coded'); + # Cleanup. unlink 'pending.data'; ok (!-r 'pending.data', 'Removed pending.data');