Support for weekly summaries and daily analysis.
build.xml --------- Added `build-shell` target. This creates a folder, `build/shell` and copies all of the required libraries and class files to that folder, excluding the groovy-all jar that conflicts with groovysh's internal classpath. Category.groovy --------------- Fixed a bug in the `addEvent()` function. CategorizationPlan.groovy ------------------------- * Rewrote this as groovy code. * Now is an abstract class instead of an interface. * Added a property, `newCatSetupFun`, which is a closure that will be run whenever this categorization plan creates a new category. A caller can set this via the constructor. * Added a function, `setupNewCategory()` that should be called inside of all `newCategory()` implementations. This allows the user to have categorization plans added to the subcategories dynamically. * Updated all the CategorizationPlan implementations to respect this new behavior. start-script.groovy ------------------- Implemented a more comprehensive analysis.
This commit is contained in:
parent
2650fca7f1
commit
5dff4de089
11
build.xml
11
build.xml
@ -5,4 +5,15 @@
|
|||||||
|
|
||||||
<target name="init"/>
|
<target name="init"/>
|
||||||
|
|
||||||
|
<target name="build-shell" depends="build">
|
||||||
|
<mkdir dir="${build.dir}/shell"/>
|
||||||
|
<copy todir="${build.dir}/shell">
|
||||||
|
<fileset dir="${build.dir}/lib/runtime/jar">
|
||||||
|
<include name="*.jar"/>
|
||||||
|
<exclude name="groovy-*.jar"/>
|
||||||
|
</fileset>
|
||||||
|
<fileset dir="${build.dir}/main/classes"/>
|
||||||
|
</copy>
|
||||||
|
</target>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#Wed, 29 Aug 2012 15:10:32 -0700
|
#Sat, 01 Sep 2012 22:19:54 -0700
|
||||||
name=time-analyzer
|
name=time-analyzer
|
||||||
version=0.1
|
version=1.0
|
||||||
build.number=5
|
build.number=0
|
||||||
lib.local=true
|
lib.local=true
|
||||||
|
@ -1,27 +1,23 @@
|
|||||||
import com.jdbernard.timeanalyzer.processors.*
|
import com.jdbernard.timeanalyzer.categories.FilteredCategory
|
||||||
import com.jdbernard.timeanalyzer.categories.*
|
import com.jdbernard.timeanalyzer.categories.TimeIntervalCategory
|
||||||
import com.jdbernard.timeanalyzer.categorizationplans.*
|
import com.jdbernard.timeanalyzer.categorizationplans.DailyCategorizationPlan
|
||||||
import com.jdbernard.timeanalyzer.chart.*
|
import com.jdbernard.timeanalyzer.categorizationplans.DescriptionBasedCategorizationPlan
|
||||||
import com.jdbernard.timeanalyzer.events.*
|
import com.jdbernard.timeanalyzer.categorizationplans.TwoLevelCategorizationPlan
|
||||||
import com.jdbernard.timestamper.core.*
|
import com.jdbernard.timeanalyzer.processors.TimelineEventProcessor
|
||||||
import com.quantumdigital.ithelp.timeanalyzer.*
|
import com.jdbernard.timestamper.core.FileTimelineSource
|
||||||
import org.joda.time.*
|
import org.jfree.chart.ChartFactory
|
||||||
import org.joda.time.format.*
|
import org.jfree.chart.ChartFrame
|
||||||
import org.jfree.chart.*
|
import org.jfree.data.general.DefaultPieDataset
|
||||||
import org.jfree.data.general.*
|
|
||||||
import org.jfree.util.SortOrder
|
import org.jfree.util.SortOrder
|
||||||
|
import org.joda.time.DateTime
|
||||||
tep = new TimelineEventProcessor()
|
import org.joda.time.Interval
|
||||||
tep.exclusions << ~/.*Home.*/
|
import org.joda.time.Period
|
||||||
|
import org.joda.time.format.PeriodFormat
|
||||||
|
|
||||||
pf = PeriodFormat.getDefault()
|
pf = PeriodFormat.getDefault()
|
||||||
|
|
||||||
topcat = new FilteredCategory("Top Category")
|
printDuration = { duration ->
|
||||||
|
pf.print(duration.toPeriod()) }
|
||||||
twoLevelCatPlan = new TwoLevelCategorizationPlan()
|
|
||||||
descriptionBasedCatPlan = new DescriptionBasedCategorizationPlan()
|
|
||||||
topcat.categorizationPlans << twoLevelCatPlan
|
|
||||||
topcat.categorizationPlans << descriptionBasedCatPlan
|
|
||||||
|
|
||||||
loadEvents = { file, processor ->
|
loadEvents = { file, processor ->
|
||||||
fileSource = new FileTimelineSource(file.toURI())
|
fileSource = new FileTimelineSource(file.toURI())
|
||||||
@ -35,9 +31,61 @@ makePieDataset = { category ->
|
|||||||
category.events.each { entry ->
|
category.events.each { entry ->
|
||||||
dpds.setValue(entry.description, entry.duration.standardSeconds) }
|
dpds.setValue(entry.description, entry.duration.standardSeconds) }
|
||||||
dpds.sortByValues(SortOrder.DESCENDING)
|
dpds.sortByValues(SortOrder.DESCENDING)
|
||||||
|
|
||||||
return dpds }
|
return dpds }
|
||||||
|
|
||||||
makeFrame = { categoryName, dataset ->
|
makePieFrame = { categoryName, category ->
|
||||||
|
def dataset = makePieDataset(category)
|
||||||
return new ChartFrame(categoryName,
|
return new ChartFrame(categoryName,
|
||||||
ChartFactory.createPieChart("Time Spent", dataset, true, true, false)) }
|
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 }
|
||||||
|
@ -47,9 +47,13 @@ public abstract class Category implements Comparable<Category> {
|
|||||||
* made and the `Event` matches.*/
|
* made and the `Event` matches.*/
|
||||||
public Event addEvent(Event event) {
|
public Event addEvent(Event event) {
|
||||||
|
|
||||||
|
def addedEvent
|
||||||
|
|
||||||
// Try first to add it to a subcategory (or create a new subcategory).
|
// Try first to add it to a subcategory (or create a new subcategory).
|
||||||
if (!addToSubcategory(event)) {
|
addedEvent = addToSubcategory(event)
|
||||||
// Cannot add to a subcategory, add to ourselves.
|
|
||||||
|
// Cannot add to a subcategory, add to ourselves.
|
||||||
|
if (!addedEvent) {
|
||||||
events << event
|
events << event
|
||||||
addedEvent = event }
|
addedEvent = event }
|
||||||
|
|
||||||
|
@ -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<Event> existingEvents)
|
||||||
|
public abstract Category newCategory(Event event, List<Event> existingEvents)
|
||||||
|
public abstract List<Event> findEventsToRecategorize(Event event, List<Event> existingEvents)
|
||||||
|
}
|
@ -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<Event> existingEvents);
|
|
||||||
Category newCategory(Event event, List<Event> existingEvents);
|
|
||||||
List<Event> findEventsToRecategorize(Event event, List<Event> existingEvents);
|
|
||||||
}
|
|
@ -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.Category
|
||||||
|
import com.jdbernard.timeanalyzer.categories.TimeIntervalCategory
|
||||||
import com.jdbernard.timeanalyzer.events.Event
|
import com.jdbernard.timeanalyzer.events.Event
|
||||||
|
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.joda.time.Interval
|
import org.joda.time.Interval
|
||||||
import org.joda.time.Period
|
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<Event> existingEvents) {
|
boolean deservesNewCategory(Event event, List<Event> existingEvents) {
|
||||||
Interval fullday = new Interval(
|
Interval fullDay = new Interval(
|
||||||
event.start.toDateMidnight(), Period.days(1))
|
event.start.toDateMidnight(), Period.days(1))
|
||||||
Interval eventIv = new Interval(
|
Interval eventIv = new Interval(
|
||||||
event.start, event.duration)
|
event.start, event.duration)
|
||||||
@ -21,8 +26,12 @@ public class DailyCategorizationPlan {
|
|||||||
Interval fullday = new Interval(
|
Interval fullday = new Interval(
|
||||||
event.start.toDateMidnight(), Period.days(1))
|
event.start.toDateMidnight(), Period.days(1))
|
||||||
|
|
||||||
return TimeIntervalCategory(
|
Category newCat = new TimeIntervalCategory(
|
||||||
event.start.toString("EEE, MMM dd", fullday)) }
|
event.start.toString("EEE, MMM dd"), fullday)
|
||||||
|
|
||||||
|
setupNewCategory(newCat)
|
||||||
|
|
||||||
|
return newCat }
|
||||||
|
|
||||||
List<Event> findEventsToRecategorize(Event event,
|
List<Event> findEventsToRecategorize(Event event,
|
||||||
List<Event> existingEvents) {
|
List<Event> existingEvents) {
|
||||||
|
@ -4,24 +4,27 @@ import com.jdbernard.timeanalyzer.categories.Category
|
|||||||
import com.jdbernard.timeanalyzer.categories.DescriptionBasedCategory
|
import com.jdbernard.timeanalyzer.categories.DescriptionBasedCategory
|
||||||
import com.jdbernard.timeanalyzer.events.Event
|
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<Event> existingEvents) {
|
public boolean deservesNewCategory(Event event, List<Event> existingEvents) {
|
||||||
def desc = event.description.replaceAll(/\p{Punct}/, '').toLowerCase()
|
def desc = event.description.replaceAll(/\p{Punct}/, '').toLowerCase()
|
||||||
return existingEvents.any {
|
return existingEvents.any {
|
||||||
it.description.replaceAll(/\p{Punct}/, '').toLowerCase() == desc }
|
it.description.replaceAll(/\p{Punct}/, '').toLowerCase() == desc } }
|
||||||
}
|
|
||||||
|
|
||||||
public Category newCategory(Event event,
|
public Category newCategory(Event event,
|
||||||
List<Event> existingEvents) {
|
List<Event> existingEvents) {
|
||||||
return new DescriptionBasedCategory(event.description)
|
def newCat = new DescriptionBasedCategory(event.description)
|
||||||
}
|
setupNewCategory(newCat)
|
||||||
|
return newCat }
|
||||||
|
|
||||||
public List<Event> findEventsToRecategorize(Event event,
|
public List<Event> findEventsToRecategorize(Event event,
|
||||||
List<Event> existingEvents) {
|
List<Event> existingEvents) {
|
||||||
def desc = event.description.replaceAll(/\p{Punct}/, '').toLowerCase()
|
def desc = event.description.replaceAll(/\p{Punct}/, '').toLowerCase()
|
||||||
return existingEvents.findAll {
|
return existingEvents.findAll {
|
||||||
it.description.replaceAll(/\p{Punct}/, '').toLowerCase() == desc }
|
it.description.replaceAll(/\p{Punct}/, '').toLowerCase() == desc } }
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,22 +4,25 @@ import com.jdbernard.timeanalyzer.categories.Category
|
|||||||
import com.jdbernard.timeanalyzer.categories.TwoLevelCategory
|
import com.jdbernard.timeanalyzer.categories.TwoLevelCategory
|
||||||
import com.jdbernard.timeanalyzer.events.Event
|
import com.jdbernard.timeanalyzer.events.Event
|
||||||
|
|
||||||
public class TwoLevelCategorizationPlan implements CategorizationPlan {
|
public class TwoLevelCategorizationPlan extends CategorizationPlan {
|
||||||
|
|
||||||
private static final def TWO_LEVEL_PATTERN = ~/(.+?):(.*)/
|
private static final def TWO_LEVEL_PATTERN = ~/(.+?):(.*)/
|
||||||
|
|
||||||
|
public TwoLevelCategorizationPlan() {}
|
||||||
|
public TwoLevelCategorizationPlan(Closure newCatSetupFun) {
|
||||||
|
super(newCatSetupFun) }
|
||||||
|
|
||||||
public boolean deservesNewCategory(Event event, List<Event> el) {
|
public boolean deservesNewCategory(Event event, List<Event> el) {
|
||||||
return event ==~ TWO_LEVEL_PATTERN
|
return event ==~ TWO_LEVEL_PATTERN }
|
||||||
}
|
|
||||||
|
|
||||||
public Category newCategory(Event event, List<Event> el) {
|
public Category newCategory(Event event, List<Event> el) {
|
||||||
def m = event.description =~ TWO_LEVEL_PATTERN
|
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<Event> findEventsToRecategorize(Event event,
|
public List<Event> findEventsToRecategorize(Event event,
|
||||||
List<Event> existingEvents) {
|
List<Event> existingEvents) {
|
||||||
def m = event.description =~ TWO_LEVEL_PATTERN
|
def m = event.description =~ TWO_LEVEL_PATTERN
|
||||||
return existingEvents.findAll { it.description ==~ /${m[0][1]}:.*/ }
|
return existingEvents.findAll { it.description ==~ /${m[0][1]}:.*/ } }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user