diff --git a/lib/compile/jcommon-1.0.16.jar b/lib/compile/jcommon-1.0.16.jar new file mode 100644 index 0000000..4cd6807 Binary files /dev/null and b/lib/compile/jcommon-1.0.16.jar differ diff --git a/resources/main/timeline.jdbernard.txt b/resources/main/timeline.jdbernard.txt index af3e2f5..9af7e8c 100644 Binary files a/resources/main/timeline.jdbernard.txt and b/resources/main/timeline.jdbernard.txt differ diff --git a/src/main/com/jdbernard/timeanalyzer/CategorizationPlan.java b/src/main/com/jdbernard/timeanalyzer/CategorizationPlan.java index 36184d1..09fbf57 100644 --- a/src/main/com/jdbernard/timeanalyzer/CategorizationPlan.java +++ b/src/main/com/jdbernard/timeanalyzer/CategorizationPlan.java @@ -5,5 +5,6 @@ import java.util.Map; public interface CategorizationPlan { boolean deservesNewCategory(Event event, List existingEntries); - Map createNewCategory(Event event, List existingEntries); + Category newCategory(Event event, List existingEntries); + List findEntriesToRecategorize(Event event, List existingEntries); } diff --git a/src/main/com/jdbernard/timeanalyzer/Category.groovy b/src/main/com/jdbernard/timeanalyzer/Category.groovy index 5d2d2cb..e070560 100644 --- a/src/main/com/jdbernard/timeanalyzer/Category.groovy +++ b/src/main/com/jdbernard/timeanalyzer/Category.groovy @@ -19,45 +19,25 @@ public abstract class Category implements Comparable { description = "Unnamed Category" } + public Category(String description) { + entries = [] + categories = [] + categorizationPlans = [] + this.description = description + } + public abstract boolean matchesEvent(Event e) public Entry addEvent(Event e) { Entry entry - // see if we have a subcategory that will hold this event + // see if we have or can create a subcategory that will hold this event entry = addToSubcategory(e) + // no, let's create a generic entry and add it if (!entry) { - // if not, do we have a plan for categorizing this event? - - // if not, do we have another entry that could be grouped with - // this one to create a new category, based on the description? - def existingEntry = entries.find - { it.description == e.description } - - // yes - if (existingEntry) { - // create the new category - def category = new DescriptionBasedCategory(e.description) - - // add the new event to the category - entry = category.addEvent(e) - - // remove the existing entry from this category and - // add it to the subcategory - this.entries -= existingEntry - category.entries << existingEntry - existingEntry.category = category - - // add the category to our list of subcategories - categories << category - } - - // no, let's create a generic entry and add it - else { - entry = new Entry(this, e) - entries << entry - } + entry = new Entry(this, e) + entries << entry } return entry @@ -78,20 +58,27 @@ public abstract class Category implements Comparable { if (matchingPlans) { // create the new category - def result = matchingPlans.createNewCategory(e, entries) + def newCategory = matchingPlans[0].newCategory(e, entries) // add it to our list of cateogries - categories << result.category + categories << newCategory - // return new entry - return result.entry + // add the new event to the category + def entry = newCategory.addEvent(e) + + // move all the entries that match the new category over + def existingEntries = matchingPlans[0].findEntriesToRecategorize(e, entries) + entries -= existingEntries + newCategory.entries.addAll(existingEntries) + existingEntries.each { it.category = newCategory } + + // return the new entry + return entry } return null } - public Entry - public Duration getDuration() { return categories.sum(new Duration(0)) { it.duration } + entries.sum(new Duration(0)) { it.duration } diff --git a/src/main/com/jdbernard/timeanalyzer/DescriptionBasedCategorizationPlan.groovy b/src/main/com/jdbernard/timeanalyzer/DescriptionBasedCategorizationPlan.groovy new file mode 100644 index 0000000..8ff6a1b --- /dev/null +++ b/src/main/com/jdbernard/timeanalyzer/DescriptionBasedCategorizationPlan.groovy @@ -0,0 +1,20 @@ +package com.jdbernard.timeanalyzer + +public class DescriptionBasedCategorizationPlan implements CategorizationPlan { + + public boolean deservesNewCategory(Event event, List existingEntries) { + return existingEntries.findAll { + it.description == event.description }.size() > 0 + } + + public Category newCategory(Event event, + List existingEntries) { + return new DescriptionBasedCategory(event.description) + } + + public List findEntriesToRecategorize(Event event, + List existingEntries) { + return existingEntries.findAll { it.description == event.description } + } + +} diff --git a/src/main/com/jdbernard/timeanalyzer/Event.java b/src/main/com/jdbernard/timeanalyzer/Event.java index b54ce38..f767d35 100644 --- a/src/main/com/jdbernard/timeanalyzer/Event.java +++ b/src/main/com/jdbernard/timeanalyzer/Event.java @@ -5,7 +5,7 @@ import org.joda.time.Duration; import org.joda.time.format.PeriodFormat; import org.joda.time.format.PeriodFormatter; -public class Event { +public class Event implements Cloneable { public String description; public String notes; @@ -19,4 +19,7 @@ public class Event { public String toString() { return description; } + + public Object clone() throws CloneNotSupportedException + { return super.clone(); } } diff --git a/src/main/com/jdbernard/timeanalyzer/FilteredCategory.groovy b/src/main/com/jdbernard/timeanalyzer/FilteredCategory.groovy index 5f0a557..4d34a2f 100644 --- a/src/main/com/jdbernard/timeanalyzer/FilteredCategory.groovy +++ b/src/main/com/jdbernard/timeanalyzer/FilteredCategory.groovy @@ -5,8 +5,7 @@ public class FilteredCategory extends Category{ List filters = [] public FilteredCategory(String description) { - super() - this.description = description + super(description) } public boolean matchesEvent(Event e) { diff --git a/src/main/com/jdbernard/timeanalyzer/GeneralCategory.groovy b/src/main/com/jdbernard/timeanalyzer/GeneralCategory.groovy index b7fdd21..9b04512 100644 --- a/src/main/com/jdbernard/timeanalyzer/GeneralCategory.groovy +++ b/src/main/com/jdbernard/timeanalyzer/GeneralCategory.groovy @@ -3,8 +3,11 @@ package com.jdbernard.timeanalyzer public class GeneralCategory extends Category { public GeneralCategory() { - super() - description = "General" + this("General") + } + + public GeneralCategory(String description) { + super(description) } public boolean matchesEvent(Event e) { true } diff --git a/src/main/com/jdbernard/timeanalyzer/TwoLevelCategorizationPlan.groovy b/src/main/com/jdbernard/timeanalyzer/TwoLevelCategorizationPlan.groovy new file mode 100644 index 0000000..d01217c --- /dev/null +++ b/src/main/com/jdbernard/timeanalyzer/TwoLevelCategorizationPlan.groovy @@ -0,0 +1,21 @@ +package com.jdbernard.timeanalyzer + +public class TwoLevelCategorizationPlan implements CategorizationPlan { + + private static final def TWO_LEVEL_PATTERN = ~/(.+?):(.*)/ + + public boolean deservesNewCategory(Event event, List el) { + return event ==~ TWO_LEVEL_PATTERN + } + + public Category newCategory(Event event, List el) { + def m = event.description =~ TWO_LEVEL_PATTERN + return new TwoLevelCategory(m[0][1]) + } + + public List findEntriesToRecategorize(Event event, + List existingEntries) { + def m = event.description =~ TWO_LEVEL_PATTERN + return existingEntries.findAll { it.description ==~ /${m[0][1]}:.*/ } + } +} diff --git a/src/main/com/jdbernard/timeanalyzer/TwoLevelCategory.groovy b/src/main/com/jdbernard/timeanalyzer/TwoLevelCategory.groovy new file mode 100644 index 0000000..8873fd2 --- /dev/null +++ b/src/main/com/jdbernard/timeanalyzer/TwoLevelCategory.groovy @@ -0,0 +1,28 @@ +package com.jdbernard.timeanalyzer + +public class TwoLevelCategory extends Category { + + private final def descriptionPattern + + public TwoLevelCategory(String heading) { + super(heading) + + descriptionPattern = ~/^${heading}:\s*(.*)/ + } + + public boolean matchesEvent(Event e) { + return e.description ==~ descriptionPattern + } + + public Entry addEvent(Event e) { + def event = e.clone() + + def m = event.description =~ descriptionPattern + + event.description = m[0][1] + + def entry = super.addEvent(event) + + return entry + } +} diff --git a/src/main/com/jdbernard/timeanalyzer/chart/Util.groovy b/src/main/com/jdbernard/timeanalyzer/chart/Util.groovy index b35cd13..d10367e 100644 --- a/src/main/com/jdbernard/timeanalyzer/chart/Util.groovy +++ b/src/main/com/jdbernard/timeanalyzer/chart/Util.groovy @@ -11,6 +11,6 @@ public class Util { DefaultPieDataset dpds = new DefaultPieDataset() category.categories { subcat -> - dpds.setValue(subcat.description, subcat. + dpds.setValue(subcat.description, subcat.duration.getMillis()) } } } diff --git a/src/main/com/quantumdigital/ithelp/timeanalyzer/ITHelpCategory.groovy b/src/main/com/quantumdigital/ithelp/timeanalyzer/ITHelpCategory.groovy deleted file mode 100644 index 352ad1a..0000000 --- a/src/main/com/quantumdigital/ithelp/timeanalyzer/ITHelpCategory.groovy +++ /dev/null @@ -1,58 +0,0 @@ -package com.quantumdigital.ithelp.timeanalyzer - -import com.jdbernard.timeanalyzer.Category -import com.jdbernard.timeanalyzer.Entry -import com.jdbernard.timeanalyzer.Event - -public class ITHelpCategory extends Category { - - private def ithelpPattern = ~/^ITHelp:\s*(.*)$/ - private def ticketPattern = ~/^ITHelp:.*#(\d+).*/ - - public boolean matchesEvent(Event e) { - return (e.description ==~ ithelpPattern) - } - - public Entry addEvent(Event e) { - - Entry entry - - // try to add to an existing category - entry = addToSubcategory(e) - - // no existing category matches - if (!entry) { - - // see if it is a new ticket entry - def ticketMatch = e.description =~ ticketPattern - - // if is a new ticket - if(ticketMatch) { - // parse the ticket number - int ticketId = ticketMatch[0][1] as int - - // create the category - TicketCategory category = new TicketCategory(ticketId) - - // add the category to our list - categories << category - - // add the event to the category - entry = category.addEvent(e) - } - - // not a new ticket, use the general logic for adding - else { - - // add a general entry to this category - entry = super.addEvent(e) - - // adjust the description (remove the ITHelp tag) - def m = entry.description =~ ithelpPattern - entry.description = m[0][1] - } - } - - return entry - } -} diff --git a/src/main/com/quantumdigital/ithelp/timeanalyzer/TicketCategorizationPlan.groovy b/src/main/com/quantumdigital/ithelp/timeanalyzer/TicketCategorizationPlan.groovy new file mode 100644 index 0000000..af2acbc --- /dev/null +++ b/src/main/com/quantumdigital/ithelp/timeanalyzer/TicketCategorizationPlan.groovy @@ -0,0 +1,30 @@ +package com.quantumdigital.ithelp.timeanalyzer + +import com.jdbernard.timeanalyzer.Category +import com.jdbernard.timeanalyzer.CategorizationPlan +import com.jdbernard.timeanalyzer.Event +import com.jdbernard.timeanalyzer.Entry + +public class TicketCategorizationPlan implements CategorizationPlan { + + private static def TICKET_PATTERN = ~/.*#(\d+).*/ + + public boolean deservesNewCategory(Event e, List el) { + return e.description ==~ TICKET_PATTERN + } + + public Category newCategory(Event e, List el) { + def m = e.description =~ TICKET_PATTERN + + return new TicketCategory(m[0][1] as int) + } + + public List findEntriesToRecategorize(Event e, + List existingEntries) { + def m = e.description =~ TICKET_PATTERN + int ticketId = m[0][1] as int + + return existingEntries.findAll { + it.description ==~ /.*#${ticketId}.*/ } + } +} diff --git a/src/main/com/quantumdigital/ithelp/timeanalyzer/TicketCategory.groovy b/src/main/com/quantumdigital/ithelp/timeanalyzer/TicketCategory.groovy index 0873205..57d1c4a 100644 --- a/src/main/com/quantumdigital/ithelp/timeanalyzer/TicketCategory.groovy +++ b/src/main/com/quantumdigital/ithelp/timeanalyzer/TicketCategory.groovy @@ -14,7 +14,7 @@ public class TicketCategory extends Category { } public boolean matchesEvent(Event e) { - return (e.description ==~ /ITHelp:.*#${ticketId}.*/) + return (e.description ==~ /.*#${ticketId}.*/) } public TicketEntry addEvent(Event e) { diff --git a/src/main/com/quantumdigital/ithelp/timeanalyzer/TicketEntry.groovy b/src/main/com/quantumdigital/ithelp/timeanalyzer/TicketEntry.groovy index 2c18396..7bcaddf 100644 --- a/src/main/com/quantumdigital/ithelp/timeanalyzer/TicketEntry.groovy +++ b/src/main/com/quantumdigital/ithelp/timeanalyzer/TicketEntry.groovy @@ -10,8 +10,7 @@ public class TicketEntry extends Entry { public TicketEntry(TicketCategory category, Event e) { super(category, e) - def m = e.description =~ /^ITHelp:\s*(.*#(\d+).*)$/ - this.description = m[0][1] - this.id = m[0][2] as int + def m = e.description =~ /.*#(\d+).*/ + this.id = m[0][1] as int } } diff --git a/startscript.groovy b/startscript.groovy index b7f487a..6fe73cd 100644 --- a/startscript.groovy +++ b/startscript.groovy @@ -16,16 +16,24 @@ fileSource = new FileTimelineSource(new File("timeline.jdbernard.txt").toURI()) timeline = fileSource.read() events = tep.process(timeline) -topcat = new FilteredCategory() +topcat = new FilteredCategory("Top Category") topcat.filters << new TimeIntervalCategoryFilter( new DateTime(2011, 1, 2, 0, 0, 0, 0), new DateTime(2011, 1, 9, 0, 0, 0, 0)) -topcat.description = "Top Category" -ithelpcat = new ITHelpCategory() -ithelpcat.description = "ITHelp" +twoLevelCatPlan = new TwoLevelCategorizationPlan() +descriptionBasedCatPlan = new DescriptionBasedCategorizationPlan() +topcat.categorizationPlans << twoLevelCatPlan +topcat.categorizationPlans << descriptionBasedCatPlan + +ithelpcat = new TwoLevelCategory("ITHelp") +ticketCatPlan = new TicketCategorizationPlan() + +ithelpcat.categorizationPlans << ticketCatPlan +ithelpcat.categorizationPlans << descriptionBasedCatPlan + topcat.categories << ithelpcat -events.each { if (topcat.matchesEvent(it)) topcat.addEvent(it) } +//events.each { if (topcat.matchesEvent(it)) topcat.addEvent(it) } makePieDataset = { category -> DefaultPieDataset dpds = new DefaultPieDataset() @@ -38,10 +46,11 @@ makePieDataset = { category -> return dpds } -topcatDataset = makePieDataset(topcat) -ithelpDataset = makePieDataset(ithelpcat) +//topcatDataset = makePieDataset(topcat) +//ithelpDataset = makePieDataset(ithelpcat) -topcatFrame = new ChartFrame("Top Category", - ChartFactory.createPieChart("Time Spent", topcatDataset, true, true, false)) -ithelpFrame = new ChartFrame("ITHelp", - ChartFactory.createPieChart("Time Spent", ithelpDataset, true, true, false)) +//topcatFrame = new ChartFrame("Top Category", + //ChartFactory.createPieChart("Time Spent", topcatDataset, true, true, false)) +//ithelpFrame = new ChartFrame("ITHelp", + //ChartFactory.createPieChart("Time Spent", ithelpDataset, true, true, false)) +