Fleshed out CategorizationPlan implementation.
Added findEntriesToRecategorize() to CategorizationPlan Refactored createNewCategory() to newCategory() on CategorizationPlan Refactored Category to use CategorizationPlans Created CatPlan for DescriptionBasedCategory Made Event cloneable. Refactored Category implementations to be aware of the single arg Category constructor. Created TwoLevelCategory, for entries which inherently have two levels of categorization in them. Created a CatPlan for TwoLevelCategory Removed the specialization implementation, ITHelpCategory: the new TwoLevelCategory is a more general version of the same. Created CatPlan for TicketCategory Fixed TicketCategory and TicketPlan to adjust for small difference between the old behavour of ITHelpCategory and new TwoLevelCategory Updated testing starter script.
This commit is contained in:
@ -5,5 +5,6 @@ import java.util.Map;
|
||||
|
||||
public interface CategorizationPlan {
|
||||
boolean deservesNewCategory(Event event, List<Entry> existingEntries);
|
||||
Map createNewCategory(Event event, List<Entry> existingEntries);
|
||||
Category newCategory(Event event, List<Entry> existingEntries);
|
||||
List<Entry> findEntriesToRecategorize(Event event, List<Entry> existingEntries);
|
||||
}
|
||||
|
@ -19,45 +19,25 @@ public abstract class Category implements Comparable<Category> {
|
||||
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<Category> {
|
||||
|
||||
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 }
|
||||
|
@ -0,0 +1,20 @@
|
||||
package com.jdbernard.timeanalyzer
|
||||
|
||||
public class DescriptionBasedCategorizationPlan implements CategorizationPlan {
|
||||
|
||||
public boolean deservesNewCategory(Event event, List<Entry> existingEntries) {
|
||||
return existingEntries.findAll {
|
||||
it.description == event.description }.size() > 0
|
||||
}
|
||||
|
||||
public Category newCategory(Event event,
|
||||
List<Entry> existingEntries) {
|
||||
return new DescriptionBasedCategory(event.description)
|
||||
}
|
||||
|
||||
public List<Entry> findEntriesToRecategorize(Event event,
|
||||
List<Entry> existingEntries) {
|
||||
return existingEntries.findAll { it.description == event.description }
|
||||
}
|
||||
|
||||
}
|
@ -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(); }
|
||||
}
|
||||
|
@ -5,8 +5,7 @@ public class FilteredCategory extends Category{
|
||||
List<CategoryFilter> filters = []
|
||||
|
||||
public FilteredCategory(String description) {
|
||||
super()
|
||||
this.description = description
|
||||
super(description)
|
||||
}
|
||||
|
||||
public boolean matchesEvent(Event e) {
|
||||
|
@ -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 }
|
||||
|
@ -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<Entry> el) {
|
||||
return event ==~ TWO_LEVEL_PATTERN
|
||||
}
|
||||
|
||||
public Category newCategory(Event event, List<Entry> el) {
|
||||
def m = event.description =~ TWO_LEVEL_PATTERN
|
||||
return new TwoLevelCategory(m[0][1])
|
||||
}
|
||||
|
||||
public List<Entry> findEntriesToRecategorize(Event event,
|
||||
List<Entry> existingEntries) {
|
||||
def m = event.description =~ TWO_LEVEL_PATTERN
|
||||
return existingEntries.findAll { it.description ==~ /${m[0][1]}:.*/ }
|
||||
}
|
||||
}
|
28
src/main/com/jdbernard/timeanalyzer/TwoLevelCategory.groovy
Normal file
28
src/main/com/jdbernard/timeanalyzer/TwoLevelCategory.groovy
Normal file
@ -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
|
||||
}
|
||||
}
|
@ -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()) }
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user