diff --git a/build.xml b/build.xml index c0bf202..ca9277f 100644 --- a/build.xml +++ b/build.xml @@ -5,4 +5,15 @@ + + + + + + + + + + + diff --git a/project.properties b/project.properties index 5ce11ac..77c8583 100644 --- a/project.properties +++ b/project.properties @@ -1,5 +1,5 @@ -#Wed, 29 Aug 2012 15:10:32 -0700 +#Sat, 01 Sep 2012 22:19:54 -0700 name=time-analyzer -version=0.1 -build.number=5 +version=1.0 +build.number=0 lib.local=true diff --git a/resources/main/start-script.groovy b/resources/main/start-script.groovy index a91d091..73da9c6 100644 --- a/resources/main/start-script.groovy +++ b/resources/main/start-script.groovy @@ -1,27 +1,23 @@ -import com.jdbernard.timeanalyzer.processors.* -import com.jdbernard.timeanalyzer.categories.* -import com.jdbernard.timeanalyzer.categorizationplans.* -import com.jdbernard.timeanalyzer.chart.* -import com.jdbernard.timeanalyzer.events.* -import com.jdbernard.timestamper.core.* -import com.quantumdigital.ithelp.timeanalyzer.* -import org.joda.time.* -import org.joda.time.format.* -import org.jfree.chart.* -import org.jfree.data.general.* +import com.jdbernard.timeanalyzer.categories.FilteredCategory +import com.jdbernard.timeanalyzer.categories.TimeIntervalCategory +import com.jdbernard.timeanalyzer.categorizationplans.DailyCategorizationPlan +import com.jdbernard.timeanalyzer.categorizationplans.DescriptionBasedCategorizationPlan +import com.jdbernard.timeanalyzer.categorizationplans.TwoLevelCategorizationPlan +import com.jdbernard.timeanalyzer.processors.TimelineEventProcessor +import com.jdbernard.timestamper.core.FileTimelineSource +import org.jfree.chart.ChartFactory +import org.jfree.chart.ChartFrame +import org.jfree.data.general.DefaultPieDataset import org.jfree.util.SortOrder - -tep = new TimelineEventProcessor() -tep.exclusions << ~/.*Home.*/ +import org.joda.time.DateTime +import org.joda.time.Interval +import org.joda.time.Period +import org.joda.time.format.PeriodFormat pf = PeriodFormat.getDefault() -topcat = new FilteredCategory("Top Category") - -twoLevelCatPlan = new TwoLevelCategorizationPlan() -descriptionBasedCatPlan = new DescriptionBasedCategorizationPlan() -topcat.categorizationPlans << twoLevelCatPlan -topcat.categorizationPlans << descriptionBasedCatPlan +printDuration = { duration -> + pf.print(duration.toPeriod()) } loadEvents = { file, processor -> fileSource = new FileTimelineSource(file.toURI()) @@ -35,9 +31,61 @@ makePieDataset = { category -> category.events.each { entry -> dpds.setValue(entry.description, entry.duration.standardSeconds) } dpds.sortByValues(SortOrder.DESCENDING) - return dpds } -makeFrame = { categoryName, dataset -> +makePieFrame = { categoryName, category -> + def dataset = makePieDataset(category) return new ChartFrame(categoryName, ChartFactory.createPieChart("Time Spent", dataset, true, true, false)) } + +analyze = { file -> + def tep = new TimelineEventProcessor() + tep.exclusions << /.*Home.*/ + + def twoLevelPlan = new TwoLevelCategorizationPlan() + def descriptionPlan = new DescriptionBasedCategorizationPlan() + + def setupCat = { cat -> + cat.categorizationPlans << twoLevelPlan + cat.categorizationPlans << descriptionPlan } + + def dailyPlan = new DailyCategorizationPlan(setupCat) + + def events = loadEvents(file, tep) + def topcat = new FilteredCategory("Time Analysis: Jonathan Bernard") + topcat.categorizationPlans << dailyPlan + topcat.categorizationPlans << twoLevelPlan + topcat.categorizationPlans << descriptionPlan + + events.each { if (topcat.matchesEvent(it)) topcat.addEvent(it) } + + return [topcat: topcat, rawEvents: events] } + +listEvents = {topcat -> + topcat.events.eachWithIndex { event, index -> + println "${index}: ${event} (${printDuration(event.duration)})" }} + +listCategories = { topcat -> + topcat.categories.eachWithIndex { cat, index -> + println "${index}: ${cat} (${printDuration(cat.duration)})" }} + +details = { topcat -> + println "Categories" + listCategories(topcat) + println "Events" + listEvents(topcat) } + +weeklySummary = { events -> + def todayMidnight = new DateTime() + def friday = todayMidnight.withDayOfWeek(5) + def monday = todayMidnight.withDayOfWeek(1) + def week = new Interval(monday, Period.days(7)) + def weekCat = new TimeIntervalCategory( + "Week of ${friday.toString('MMM dd, yyyy')}", week) + + weekCat.categorizationPlans << new TwoLevelCategorizationPlan() + weekCat.categorizationPlans << new DescriptionBasedCategorizationPlan() + + events.each { if (weekCat.matchesEvent(it)) weekCat.addEvent(it) } + + return weekCat } diff --git a/src/main/com/jdbernard/timeanalyzer/categories/Category.groovy b/src/main/com/jdbernard/timeanalyzer/categories/Category.groovy index 3b88ab2..d5cfe2a 100644 --- a/src/main/com/jdbernard/timeanalyzer/categories/Category.groovy +++ b/src/main/com/jdbernard/timeanalyzer/categories/Category.groovy @@ -47,9 +47,13 @@ public abstract class Category implements Comparable { * made and the `Event` matches.*/ public Event addEvent(Event event) { + def addedEvent + // Try first to add it to a subcategory (or create a new subcategory). - if (!addToSubcategory(event)) { - // Cannot add to a subcategory, add to ourselves. + addedEvent = addToSubcategory(event) + + // Cannot add to a subcategory, add to ourselves. + if (!addedEvent) { events << event addedEvent = event } diff --git a/src/main/com/jdbernard/timeanalyzer/categorizationplans/CategorizationPlan.groovy b/src/main/com/jdbernard/timeanalyzer/categorizationplans/CategorizationPlan.groovy new file mode 100644 index 0000000..d79a125 --- /dev/null +++ b/src/main/com/jdbernard/timeanalyzer/categorizationplans/CategorizationPlan.groovy @@ -0,0 +1,24 @@ +package com.jdbernard.timeanalyzer.categorizationplans; + +import com.jdbernard.timeanalyzer.categories.Category; +import com.jdbernard.timeanalyzer.events.Event; + +import java.util.List; +import java.util.Map; + +public abstract class CategorizationPlan { + + public CategorizationPlan() {} + public CategorizationPlan(Closure newCatSetupFun) { + newCategorySetupFun = newCatSetupFun } + + protected Closure newCategorySetupFun + + protected void setupNewCategory(Category cat) { + if (newCategorySetupFun) newCategorySetupFun(cat) } + + + public abstract boolean deservesNewCategory(Event event, List existingEvents) + public abstract Category newCategory(Event event, List existingEvents) + public abstract List findEventsToRecategorize(Event event, List existingEvents) +} diff --git a/src/main/com/jdbernard/timeanalyzer/categorizationplans/CategorizationPlan.java b/src/main/com/jdbernard/timeanalyzer/categorizationplans/CategorizationPlan.java deleted file mode 100644 index 351211d..0000000 --- a/src/main/com/jdbernard/timeanalyzer/categorizationplans/CategorizationPlan.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.jdbernard.timeanalyzer.categorizationplans; - -import com.jdbernard.timeanalyzer.categories.Category; -import com.jdbernard.timeanalyzer.events.Event; - -import java.util.List; -import java.util.Map; - -public interface CategorizationPlan { - boolean deservesNewCategory(Event event, List existingEvents); - Category newCategory(Event event, List existingEvents); - List findEventsToRecategorize(Event event, List existingEvents); -} diff --git a/src/main/com/jdbernard/timeanalyzer/categorizationplans/DailyCategorizationPlan.groovy b/src/main/com/jdbernard/timeanalyzer/categorizationplans/DailyCategorizationPlan.groovy index f4a10b5..0fee24e 100644 --- a/src/main/com/jdbernard/timeanalyzer/categorizationplans/DailyCategorizationPlan.groovy +++ b/src/main/com/jdbernard/timeanalyzer/categorizationplans/DailyCategorizationPlan.groovy @@ -1,16 +1,21 @@ -package com.jdbernard.timeanalyzer.categorizationplans; +package com.jdbernard.timeanalyzer.categorizationplans import com.jdbernard.timeanalyzer.categories.Category +import com.jdbernard.timeanalyzer.categories.TimeIntervalCategory import com.jdbernard.timeanalyzer.events.Event import org.joda.time.DateTime import org.joda.time.Interval import org.joda.time.Period -public class DailyCategorizationPlan { +public class DailyCategorizationPlan extends CategorizationPlan { + + public DailyCategorizationPlan() {} + public DailyCategorizationPlan(Closure newCatSetupFun) { + super(newCatSetupFun) } boolean deservesNewCategory(Event event, List existingEvents) { - Interval fullday = new Interval( + Interval fullDay = new Interval( event.start.toDateMidnight(), Period.days(1)) Interval eventIv = new Interval( event.start, event.duration) @@ -21,8 +26,12 @@ public class DailyCategorizationPlan { Interval fullday = new Interval( event.start.toDateMidnight(), Period.days(1)) - return TimeIntervalCategory( - event.start.toString("EEE, MMM dd", fullday)) } + Category newCat = new TimeIntervalCategory( + event.start.toString("EEE, MMM dd"), fullday) + + setupNewCategory(newCat) + + return newCat } List findEventsToRecategorize(Event event, List existingEvents) { diff --git a/src/main/com/jdbernard/timeanalyzer/categorizationplans/DescriptionBasedCategorizationPlan.groovy b/src/main/com/jdbernard/timeanalyzer/categorizationplans/DescriptionBasedCategorizationPlan.groovy index 11bb7d6..914e007 100644 --- a/src/main/com/jdbernard/timeanalyzer/categorizationplans/DescriptionBasedCategorizationPlan.groovy +++ b/src/main/com/jdbernard/timeanalyzer/categorizationplans/DescriptionBasedCategorizationPlan.groovy @@ -4,24 +4,27 @@ import com.jdbernard.timeanalyzer.categories.Category import com.jdbernard.timeanalyzer.categories.DescriptionBasedCategory import com.jdbernard.timeanalyzer.events.Event -public class DescriptionBasedCategorizationPlan implements CategorizationPlan { +public class DescriptionBasedCategorizationPlan extends CategorizationPlan { + + public DescriptionBasedCategorizationPlan() {} + public DescriptionBasedCategorizationPlan(Closure newCatSetupFun) { + super(newCatSetupFun) } public boolean deservesNewCategory(Event event, List existingEvents) { def desc = event.description.replaceAll(/\p{Punct}/, '').toLowerCase() return existingEvents.any { - it.description.replaceAll(/\p{Punct}/, '').toLowerCase() == desc } - } + it.description.replaceAll(/\p{Punct}/, '').toLowerCase() == desc } } public Category newCategory(Event event, List existingEvents) { - return new DescriptionBasedCategory(event.description) - } + def newCat = new DescriptionBasedCategory(event.description) + setupNewCategory(newCat) + return newCat } public List findEventsToRecategorize(Event event, List existingEvents) { def desc = event.description.replaceAll(/\p{Punct}/, '').toLowerCase() return existingEvents.findAll { - it.description.replaceAll(/\p{Punct}/, '').toLowerCase() == desc } - } + it.description.replaceAll(/\p{Punct}/, '').toLowerCase() == desc } } } diff --git a/src/main/com/jdbernard/timeanalyzer/categorizationplans/TwoLevelCategorizationPlan.groovy b/src/main/com/jdbernard/timeanalyzer/categorizationplans/TwoLevelCategorizationPlan.groovy index ee7b182..f0ec1f7 100644 --- a/src/main/com/jdbernard/timeanalyzer/categorizationplans/TwoLevelCategorizationPlan.groovy +++ b/src/main/com/jdbernard/timeanalyzer/categorizationplans/TwoLevelCategorizationPlan.groovy @@ -4,22 +4,25 @@ import com.jdbernard.timeanalyzer.categories.Category import com.jdbernard.timeanalyzer.categories.TwoLevelCategory import com.jdbernard.timeanalyzer.events.Event -public class TwoLevelCategorizationPlan implements CategorizationPlan { +public class TwoLevelCategorizationPlan extends CategorizationPlan { private static final def TWO_LEVEL_PATTERN = ~/(.+?):(.*)/ + public TwoLevelCategorizationPlan() {} + public TwoLevelCategorizationPlan(Closure newCatSetupFun) { + super(newCatSetupFun) } + public boolean deservesNewCategory(Event event, List el) { - return event ==~ TWO_LEVEL_PATTERN - } + 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]) - } + def newCat = new TwoLevelCategory(m[0][1]) + setupNewCategory(newCat) + return newCat } public List findEventsToRecategorize(Event event, List existingEvents) { def m = event.description =~ TWO_LEVEL_PATTERN - return existingEvents.findAll { it.description ==~ /${m[0][1]}:.*/ } - } + return existingEvents.findAll { it.description ==~ /${m[0][1]}:.*/ } } }