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:
		
							
								
								
									
										11
									
								
								build.xml
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								build.xml
									
									
									
									
									
								
							@@ -5,4 +5,15 @@
 | 
			
		||||
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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 }
 | 
			
		||||
 
 | 
			
		||||
@@ -47,9 +47,13 @@ public abstract class Category implements Comparable<Category> {
 | 
			
		||||
     * 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 }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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.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<Event> 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<Event> findEventsToRecategorize(Event event,
 | 
			
		||||
    List<Event> existingEvents) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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<Event> 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<Event> existingEvents) {
 | 
			
		||||
        return new DescriptionBasedCategory(event.description)
 | 
			
		||||
    }
 | 
			
		||||
        def newCat = new DescriptionBasedCategory(event.description)
 | 
			
		||||
        setupNewCategory(newCat)
 | 
			
		||||
        return newCat }
 | 
			
		||||
 | 
			
		||||
    public List<Event> findEventsToRecategorize(Event event,
 | 
			
		||||
    List<Event> 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 } }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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<Event> el) {
 | 
			
		||||
        return event ==~ TWO_LEVEL_PATTERN
 | 
			
		||||
    }
 | 
			
		||||
        return event ==~ TWO_LEVEL_PATTERN }
 | 
			
		||||
 | 
			
		||||
    public Category newCategory(Event event, List<Event> 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<Event> findEventsToRecategorize(Event event,
 | 
			
		||||
    List<Event> existingEvents) {
 | 
			
		||||
        def m = event.description =~ TWO_LEVEL_PATTERN
 | 
			
		||||
        return existingEvents.findAll { it.description ==~ /${m[0][1]}:.*/ }
 | 
			
		||||
    }
 | 
			
		||||
        return existingEvents.findAll { it.description ==~ /${m[0][1]}:.*/ } }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user