diff --git a/.gitignore b/.gitignore index 6169d30..fb81760 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +release/ *.sw* */build/ diff --git a/pit-cli/.gitignore b/pit-cli/.gitignore deleted file mode 100644 index 90dc57c..0000000 --- a/pit-cli/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -release/ -build/ -*.sw? diff --git a/pit-cli/lib/compile/jar/joda-time-2.0.jar b/pit-cli/lib/compile/jar/joda-time-2.0.jar new file mode 100644 index 0000000..169a7a4 Binary files /dev/null and b/pit-cli/lib/compile/jar/joda-time-2.0.jar differ diff --git a/pit-cli/lib/runtime/jar/joda-time-2.0.jar b/pit-cli/lib/runtime/jar/joda-time-2.0.jar new file mode 100644 index 0000000..169a7a4 Binary files /dev/null and b/pit-cli/lib/runtime/jar/joda-time-2.0.jar differ diff --git a/pit-cli/project.properties b/pit-cli/project.properties index 5798ad6..e77bdfd 100755 --- a/pit-cli/project.properties +++ b/pit-cli/project.properties @@ -1,9 +1,9 @@ -#Tue, 22 Nov 2011 14:40:19 -0600 +#Wed, 07 Dec 2011 17:57:54 -0600 build.dir=build src.dir=src build.jar=pit-cli-${application.version}.${build.number}.jar -build.number=4 -version=3.0.0 +build.number=13 +version=3.1.0 name=pit-cli lib.dir=lib lib.local=true diff --git a/pit-cli/resources/main/logback.groovy b/pit-cli/resources/main/logback.groovy new file mode 100644 index 0000000..e69de29 diff --git a/pit-cli/src/main/com/jdbernard/pit/PersonalIssueTrackerCLI.groovy b/pit-cli/src/main/com/jdbernard/pit/PersonalIssueTrackerCLI.groovy index 6574b49..79e89c2 100644 --- a/pit-cli/src/main/com/jdbernard/pit/PersonalIssueTrackerCLI.groovy +++ b/pit-cli/src/main/com/jdbernard/pit/PersonalIssueTrackerCLI.groovy @@ -2,6 +2,8 @@ package com.jdbernard.pit import com.jdbernard.pit.file.* +import org.joda.time.DateMidnight + import static java.lang.Math.max import static java.lang.Math.min @@ -36,12 +38,41 @@ cli.C(argName: 'new-category', longOpt: 'set-category', args: 1, cli.S(argName: 'new-status', longOpt: 'set-status', args: 1, required: false, 'Modify the status of the selected issues.') cli.n(longOpt: 'new-issue', 'Create a new issue.') +cli.o(longOpt: 'order', argName: 'order', args: 1, required: false, + 'Order (sort) the results by the given properties. Provide a comma-' + + 'seperated list of property names to sort by in order of importance. The' + + ' basic properties (id, category, status, and priority) can be given' + + ' using their one-letter forms (i,c,s,p) for brevity. For example:' + + ' "-o Due,p,c" would sort first by the extended property "Due", then for' + + ' items that have the same "Due" value it would sort by priority, then' + + ' by category.') cli.d(longOpt: 'dir', argName: 'dir', args: 1, required: false, 'Use as the base directory (defaults to current directory).') +cli.D(longOpt: 'daily-list', 'Print a Daily Task list based on issue Due and' + + ' Reminder properties.') +cli._(longOpt: 'dl-scheduled', 'Show scheduled tasks in the daily list (all' + + ' are shown by default).') +cli._(longOpt: 'dl-due', 'Show due tasks in the daily list (all are shown by' + + ' default).') +cli._(longOpt: 'dl-reminder', 'Show upcoming tasks in the daily list (all ' + + ' are shown by default).') +cli._(longOpt: 'dl-open', 'Show open tasks in the daily list (all are shown ' + + ' by default).') +cli._(longOpt: 'dl-hide-scheduled', 'Hide scheduled tasks in the daily list' + + ' (all are shown by default).') +cli._(longOpt: 'dl-hide-due', 'Show due tasks in the daily list (all are' + + ' shown by default).') +cli._(longOpt: 'dl-hide-reminder', 'Show upcoming tasks in the daily list' + + ' (all are shown by default).') +cli._(longOpt: 'dl-hide-open', 'Show open tasks in the daily list (all are' + + ' shown by default).') cli._(longOpt: 'version', 'Display PIT version information.') -// -------- parse CLI options -------- // -def VERSION = "3.0.0" +// =================================== // +// ======== Parse CLI Options ======== // +// =================================== // + +def VERSION = "3.1.0" def opts = cli.parse(args) def issuedb = [:] def workingDir = new File('.') @@ -96,7 +127,7 @@ try { selectOpts.status = selectOpts.status.collect { Status.toStatus(it) } } catch (Exception e) { println "Invalid status option: '-s ${e.localizedMessage}'." - println "Valid options are: \b${Status.values().join(', ')}" + print "Valid options are: \n${Status.values().join(', ')}" println " (abbreviations are accepted.)" System.exit(1) } @@ -115,6 +146,17 @@ if (opts.r) { selectOpts.projects = // read and parse the ids filter if (opts.i) { selectOpts.ids = opts.i.split(/[,\s]/).asType(List.class) } +// read and parse sort criteria +if (opts.o) { + def sortProps = opts.o.split(',') + selectOpts.issueSorter = sortProps.collect { prop -> + switch (prop) { + case ~/^i$/: return { issue -> issue.id } + case ~/^p$/: return { issue -> issue.priority } + case ~/^s$/: return { issue -> issue.status } + case ~/^c$/: return { issue -> issue.category } + default: return { issue -> issue[prop] } }}} + // TODO: accept projects value from input // read and parse the category to assign @@ -152,7 +194,11 @@ if (opts.d) { return -1 } } def EOL = System.getProperty('line.separator') -// -------- Actions -------- // + +// ========================= // +// ======== Actions ======== // +// ========================= // + // list version information first if (opts.version) { @@ -191,7 +237,104 @@ if (opts.l) { // print all projects issuedb.eachProject(filter) { printProject(it, "") } } -// new issues third +// daily list second +else if (opts.D) { + + // Parse daily list specific display options + def visibleSections = [] + def suppressedSections + + // Parse the additive options first. + if (opts.'dl-scheduled') { visibleSections << 'scheduled' } + if (opts.'dl-due') { visibleSections << 'due' } + if (opts.'dl-reminder') { visibleSections << 'reminder' } + if (opts.'dl-open') { visibleSections << 'open' } + + // If the user did not add any sections assume they want them all. + if (visibleSections.size() == 0) { + visibleSections = ['scheduled', 'due', 'reminder', 'open'] } + + // Now go through the negative options. + if (opts.'dl-hide-scheduled') { visibleSections -= 'scheduled' } + if (opts.'dl-hide-due') { visibleSections -= 'due' } + if (opts.'dl-hide-reminder') { visibleSections -= 'reminder' } + if (opts.'dl-hide-open') { visibleSections -= 'open' } + + // If the user did not specifically ask for a status filter, we want a + // different filter for the default when we are doing a daily list. + if (!opts.s) { filter.status = [Status.NEW, Status.VALIDATION_REQUIRED] } + + // If the user did not give a specific sorting order, define our own. + if (!opts.o) { filter.issueSorter = [ {it.due}, {it.priority}, {it.id} ] } + + // Get our issues + def allIssues = issuedb.getAllIssues(filter) + + // Set up our time interval. + def today = new DateMidnight() + def tomorrow = today.plusDays(1) + + def scheduledToday = [] + def dueToday = [] + def reminderToday = [] + def notDueOrReminder = [] + + def printIssue = { issue -> + if (issue.due) println "${issue.due.toString('EEE, MM/dd')} -- ${issue}" + else println " -- ${issue}" } + + // Sort the issues into seperate lists based on their due dates and + // reminders. + allIssues.each { issue -> + // Find the issues that are scheduled for today. + if (issue.scheduled && issue.scheduled < tomorrow) { + scheduledToday << issue } + + // Find the issues that are due today or are past due. + else if (issue.due && issue.due < tomorrow) { dueToday << issue } + + // Find the issues that are not yet due but have a reminder for today or + // days past. + else if (issue.reminder && issue.reminder < tomorrow) { + reminderToday << issue } + + // All the others (not due and no reminder). + else notDueOrReminder << issue } + + // Print the issues + if (visibleSections.contains('scheduled') && scheduledToday.size() > 0) { + println "Tasks Scheduled for Today" + println "-------------------------" + + scheduledToday.each { printIssue(it) } + + println "" } + + if (visibleSections.contains('due') && dueToday.size() > 0) { + println "Tasks Due Today" + println "---------------" + + dueToday.each { printIssue(it) } + + println ""} + + if (visibleSections.contains('reminder') && reminderToday.size() > 0) { + println "Upcoming Tasks" + println "--------------" + + reminderToday.each { printIssue(it) } + + println ""} + + if (visibleSections.contains('open') && notDueOrReminder.size() > 0) { + println "Other Open Issues" + println "-----------------" + + notDueOrReminder.each { printIssue(it) } + + println "" }} + +// new issues fourth else if (opts.n) { def cat, priority String text = "" @@ -243,7 +386,7 @@ else { it.priority = assignOpts.priority println "[${it}] -- set priority to ${assignOpts.priority}"} - // change third + // change category else if (opts.C) issuedb.walkProject(filter) { it.category = assignOpts.cat println "[${it}] -- set category to ${assignOpts.category}"} @@ -251,5 +394,4 @@ else { // change status else if (opts.S) issuedb.walkProject(filter) { it.status = assignOpts.status - println "[${it}] -- set status to ${assignOpts.status}"} -}} + println "[${it}] -- set status to ${assignOpts.status}"} }}