Merge branch 'temp'
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
<project name="Time Analyzer" default="compile">
|
||||
|
||||
<property file="project.properties"/>
|
||||
<import file="jdb-build-1.9.xml"/>
|
||||
|
||||
<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>
|
||||
@@ -0,0 +1,248 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<project name="Jonathan Bernard Build Common"
|
||||
xmlns:ivy="antlib:org.apache.ivy.ant">
|
||||
|
||||
<property environment="env"/>
|
||||
|
||||
<!--======== INIT TARGETS ========-->
|
||||
<target name="-init" depends="-common-init,init"/>
|
||||
|
||||
<target name="-common-init">
|
||||
<!-- Set default values for some key properties. Since properties are
|
||||
write once, any value set before this point takes precedence. -->
|
||||
|
||||
<property name="versioning.file" value="project.properties"/>
|
||||
|
||||
<property name="src.dir" value="${basedir}/src"/>
|
||||
<property name="build.dir" value="${basedir}/build"/>
|
||||
<property name="lib.dir" value="${basedir}/lib"/>
|
||||
<property name="resources.dir" value="${basedir}/resources"/>
|
||||
<property name="splash.image" value="splash.png"/>
|
||||
|
||||
<!--======== PATHS ========-->
|
||||
<path id="groovy.classpath">
|
||||
<fileset dir="${env.GROOVY_HOME}/lib">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</path>
|
||||
|
||||
<path id="groovy.embeddable">
|
||||
<fileset dir="${env.GROOVY_HOME}/embeddable">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</path>
|
||||
|
||||
<path id="compile-libs">
|
||||
<fileset dir="${build.dir}/lib/compile/jar">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</path>
|
||||
|
||||
<path id="runtime-libs">
|
||||
<fileset dir="${build.dir}/lib/runtime/jar">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</path>
|
||||
|
||||
</target>
|
||||
|
||||
<target name="-init-groovy">
|
||||
<taskdef name="groovyc" classpathref="groovy.classpath"
|
||||
classname="org.codehaus.groovy.ant.Groovyc"/>
|
||||
|
||||
<taskdef name="groovy" classpathref="groovy.classpath"
|
||||
classname="org.codehaus.groovy.ant.Groovy"/>
|
||||
</target>
|
||||
|
||||
<target name="init"/>
|
||||
|
||||
<target name="clean" depends="-init">
|
||||
<delete dir="${build.dir}"/>
|
||||
</target>
|
||||
|
||||
<!--======== LIBRARY TARGETS ========-->
|
||||
<target name="-lib" depends="-lib-local,-lib-ivy,lib"/>
|
||||
|
||||
<target name="lib"/>
|
||||
|
||||
<target name="-init-ivy">
|
||||
<ivy:settings id="ivy.settings" file="ivysettings.xml"/>
|
||||
</target>
|
||||
|
||||
<target name="-lib-ivy" depends="-init-ivy" unless="${lib.local}">
|
||||
<ivy:retrieve settingsRef="ivy.settings"
|
||||
pattern="${lib.dir}/[conf]/[type]/[artifact]-[revision].[ext]"
|
||||
conf="compile,runtime"/>
|
||||
</target>
|
||||
|
||||
<target name="-lib-groovy" if="${lib.local}">
|
||||
<copy todir="${build.dir}/lib/runtime/jar">
|
||||
<fileset dir="${env.GROOVY_HOME}/embeddable"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<target name="-lib-local" if="${lib.local}">
|
||||
<echo message="Resolving libraries locally."/>
|
||||
<mkdir dir="${build.dir}/lib/compile/jar"/>
|
||||
<mkdir dir="${build.dir}/lib/runtime/jar"/>
|
||||
<copy todir="${build.dir}/lib/compile/jar" failonerror="false">
|
||||
<fileset dir="${lib.dir}/compile/jar"/>
|
||||
</copy>
|
||||
|
||||
<copy todir="${build.dir}/lib/runtime/jar" failonerror="false">
|
||||
<fileset dir="${lib.dir}/runtime/jar"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<!--======== VERSIONING TARGETS ========-->
|
||||
<target name="increment-build-number" depends="-init">
|
||||
<propertyfile file="${versioning.file}">
|
||||
<entry key="build.number" default="0" type="int" value="1"
|
||||
operation="+"/>
|
||||
</propertyfile>
|
||||
</target>
|
||||
|
||||
<target name="set-version" depends="-init">
|
||||
<input
|
||||
message="The current version is ${version}. Enter a new version: "
|
||||
addproperty="new-version"/>
|
||||
<propertyfile file="${versioning.file}">
|
||||
<entry key="version" value="${new-version}" operation="="
|
||||
type="string"/>
|
||||
<entry key="build.number" value="0" type="int" operation="="/>
|
||||
</propertyfile>
|
||||
</target>
|
||||
|
||||
<!--======== COMPILATION TARGETS ========-->
|
||||
<target name="-compile-groovy" depends="-init,-init-groovy,-lib,-lib-groovy">
|
||||
<mkdir dir="${build.dir}/main/classes"/>
|
||||
<groovyc srcdir="${src.dir}/main" destdir="${build.dir}/main/classes"
|
||||
fork="true" includeAntRuntime="false">
|
||||
|
||||
<classpath>
|
||||
<path refid="groovy.classpath"/>
|
||||
<path refid="compile-libs"/>
|
||||
</classpath>
|
||||
<javac/>
|
||||
</groovyc>
|
||||
</target>
|
||||
|
||||
<target name="-compile-java" depends="-init,-lib">
|
||||
<mkdir dir="${build.dir}/main/classes"/>
|
||||
<javac srcdir="${src.dir}/main" destdir="${build.dir}/main/classes"
|
||||
includeAntRuntime="false" classpathref="compile-libs"/>
|
||||
</target>
|
||||
|
||||
<target name="compile" depends="-compile-groovy"/>
|
||||
|
||||
<!--======== JUNIT TARGETS ========-->
|
||||
<target name="-compile-tests-groovy" depends="-init,compile">
|
||||
<mkdir dir="${build.dir}/test/classes"/>
|
||||
<groovyc srcdir="${src.dir}/test" destdir="${build.dir}/test/classes"
|
||||
includeAntRuntime="false">
|
||||
|
||||
<classpath>
|
||||
<path refid="groovy.classpath"/>
|
||||
<path refid="compile-libs"/>
|
||||
<path location="${build.dir}/main/classes"/>
|
||||
</classpath>
|
||||
</groovyc>
|
||||
</target>
|
||||
|
||||
<target name="-compile-tests-java" depends="-init,compile">
|
||||
<mkdir dir="${build.dir}/test/classes"/>
|
||||
<javac srcdir="${src.dir}/test" destdir="${build.dir}/test/classes"
|
||||
includeAntRuntime="false">
|
||||
<classpath>
|
||||
<path refid="compile-libs"/>
|
||||
<path location="${build.dir}/main/classes"/>
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="compile-tests" depends="-compile-tests-groovy"/>
|
||||
|
||||
<target name="run-tests" depends="compile-tests,resources-test">
|
||||
<junit printsummary="true">
|
||||
<classpath>
|
||||
<path refid="groovy.classpath"/>
|
||||
<path refid="compile-libs"/>
|
||||
<path location="${build.dir}/main/classes"/>
|
||||
<path location="${build.dir}/test/classes"/>
|
||||
</classpath>
|
||||
<formatter type="plain" usefile="false"/>
|
||||
<batchtest>
|
||||
<fileset dir="${build.dir}/test/classes">
|
||||
<include name="**/*"/>
|
||||
</fileset>
|
||||
</batchtest>
|
||||
</junit>
|
||||
</target>
|
||||
|
||||
<!--======== RESOURCES TARGETS ========-->
|
||||
|
||||
<target name="resources" depends="-init">
|
||||
<mkdir dir="${build.dir}/main/classes"/>
|
||||
<copy todir="${build.dir}/main/classes" failonerror="false">
|
||||
<fileset dir="${resources.dir}/main/"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<target name="resources-test" depends="-init">
|
||||
<mkdir dir="${build.dir}/test/classes"/>
|
||||
<copy todir="${build.dir}/test/classes" failonerror="false">
|
||||
<fileset dir="${resources.dir}/test/"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<!--======== BUILD TARGETS ========-->
|
||||
<target name="-build-modular-lib" unless="executable.jar"
|
||||
depends="compile,increment-build-number,resources">
|
||||
|
||||
<jar destfile="${build.dir}/${name}-${version}.${build.number}.jar"
|
||||
basedir="${build.dir}/main/classes"/>
|
||||
</target>
|
||||
|
||||
<target name="-build-modular-executable" if="executable.jar"
|
||||
depends="compile,increment-build-number,resources">
|
||||
|
||||
<pathconvert property="jar.classpath" pathsep=" " refid="runtime-libs">
|
||||
<mapper>
|
||||
<chainedmapper>
|
||||
<!-- remove absolute path -->
|
||||
<flattenmapper />
|
||||
|
||||
<!-- add lib/ prefix -->
|
||||
<globmapper from="*" to="lib/*" />
|
||||
</chainedmapper>
|
||||
</mapper>
|
||||
</pathconvert>
|
||||
|
||||
<jar destfile="${build.dir}/${name}-${version}.${build.number}.jar"
|
||||
basedir="${build.dir}/main/classes">
|
||||
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="${main.class}"/>
|
||||
<attribute name="Class-Path" value="${jar.classpath}"/>
|
||||
<attribute name="SplashScreen-Image" value="${splash.image}"/>
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="-build-modular"
|
||||
depends="-build-modular-lib,-build-modular-executable"/>
|
||||
|
||||
<target name="-build-packed-libs"
|
||||
depends="compile,increment-build-number,resources">
|
||||
|
||||
<unjar destdir="${build.dir}/main/classes">
|
||||
<fileset dir="${build.dir}/lib/runtime/jar"/>
|
||||
</unjar>
|
||||
|
||||
<jar destfile="${build.dir}/${name}-${version}.${build.number}.jar"
|
||||
basedir="${build.dir}/main/classes"/>
|
||||
</target>
|
||||
|
||||
<target name="build" depends="-build-modular"/>
|
||||
|
||||
</project>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
#Mon, 03 Sep 2012 23:38:34 -0500
|
||||
name=time-analyzer
|
||||
version=1.0
|
||||
build.number=11
|
||||
lib.local=true
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,84 @@
|
||||
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
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.Interval
|
||||
import org.joda.time.Period
|
||||
import org.joda.time.format.PeriodFormat
|
||||
import static com.jdbernard.timeanalyzer.chart.Util.*
|
||||
|
||||
loadEvents = { file, processor ->
|
||||
fileSource = new FileTimelineSource(file.toURI())
|
||||
timeline = fileSource.read()
|
||||
return processor.process(timeline) }
|
||||
|
||||
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 ->
|
||||
String result = ""
|
||||
topcat.events.eachWithIndex { event, index ->
|
||||
result += "${index}: ${event}\n" }
|
||||
|
||||
return result }
|
||||
|
||||
listCategories = { topcat ->
|
||||
String result = ""
|
||||
topcat.categories.eachWithIndex { cat, index ->
|
||||
result += "${index}: ${cat}\n" }
|
||||
|
||||
return result }
|
||||
|
||||
details = { topcat ->
|
||||
def result = "Categories\n" + listCategories(topcat)
|
||||
result += "Events\n" + listEvents(topcat)
|
||||
|
||||
return "\n" + result }
|
||||
|
||||
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 }
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.jdbernard.timeanalyzer.categories
|
||||
|
||||
import com.jdbernard.timeanalyzer.events.Event
|
||||
import org.joda.time.Duration
|
||||
import org.joda.time.format.PeriodFormat
|
||||
import org.joda.time.format.PeriodFormatter
|
||||
|
||||
/**
|
||||
* A `Category` represents a collection of like `Events` and sub-categories.
|
||||
*/
|
||||
public abstract class Category implements Comparable<Category> {
|
||||
|
||||
public static PeriodFormatter periodFormatter = PeriodFormat.getDefault()
|
||||
|
||||
/** List of events directly under this category. */
|
||||
public List events
|
||||
|
||||
/** List of sub-categories under this category. */
|
||||
public List categories
|
||||
|
||||
/** List of `CategorizationPlan`s to use when adding new `Event`s to the
|
||||
* category. */
|
||||
public List categorizationPlans
|
||||
|
||||
/** A end-user-friendly text description of the category.*/
|
||||
public String description
|
||||
|
||||
public Category() { this("Unamed Category") }
|
||||
|
||||
public Category(String description) {
|
||||
events = []
|
||||
categories = []
|
||||
categorizationPlans = []
|
||||
this.description = description }
|
||||
|
||||
/**
|
||||
* Does the given event belong in this category?
|
||||
* @param e `Event` being considered.
|
||||
* @return **`true`** if this event belongs in this category, **`false`**
|
||||
* otherwise.
|
||||
*/
|
||||
public abstract boolean matchesEvent(Event e)
|
||||
|
||||
/**
|
||||
* Add an event to this category. This method does not check to see if the
|
||||
* `Event` matches this category. It assumed that the check has already been
|
||||
* 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).
|
||||
addedEvent = addToSubcategory(event)
|
||||
|
||||
// Cannot add to a subcategory, add to ourselves.
|
||||
if (!addedEvent) {
|
||||
events << event
|
||||
addedEvent = event }
|
||||
|
||||
return addedEvent }
|
||||
|
||||
public Event addToSubcategory(Event e) {
|
||||
|
||||
// find all matching subcategories
|
||||
def matchingCategories = categories.findAll { it.matchesEvent(e) }
|
||||
|
||||
if (matchingCategories) {
|
||||
return matchingCategories[0].addEvent(e) }
|
||||
|
||||
// no matching subcategories, can we create a new one based on one
|
||||
// of our plans?
|
||||
def matchingPlans = categorizationPlans.findAll {
|
||||
it.deservesNewCategory(e, events) }
|
||||
|
||||
if (matchingPlans) {
|
||||
// create the new category
|
||||
def newCategory = matchingPlans[0].newCategory(e, events)
|
||||
|
||||
// add it to our list of cateogries
|
||||
categories << newCategory
|
||||
|
||||
// add the new event to the category
|
||||
def addedEvent = newCategory.addEvent(e)
|
||||
|
||||
// move all the events that match the new category over
|
||||
def existingEvents = matchingPlans[0].findEventsToRecategorize(e, events)
|
||||
events -= existingEvents
|
||||
existingEvents.each { newCategory.addEvent(it) }
|
||||
|
||||
// return the new entry
|
||||
return addedEvent }
|
||||
|
||||
return null }
|
||||
|
||||
public Category filter(List<CategoryFilter> filters) {
|
||||
|
||||
// create new filtered category
|
||||
FilteredCategory fc = new FilteredCategory(description)
|
||||
fc.filters = filters
|
||||
|
||||
// filter all events and add them to the category
|
||||
fc.events
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
public Category filter(CategoryFilter filter) { return filter([filter]) }
|
||||
|
||||
public Category filter(Closure c) { return filter(c as CategoryFilter) }
|
||||
|
||||
public Duration getDuration() {
|
||||
return categories.sum(new Duration(0)) { it.duration } +
|
||||
events.sum(new Duration(0)) { it.duration } }
|
||||
|
||||
public int compareTo(Category other) {
|
||||
return this.getDuration().compareTo(other.getDuration()) }
|
||||
|
||||
public String toString() {
|
||||
String period = periodFormatter.print(this.duration.toPeriod())
|
||||
return "${description} (${period})" }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.jdbernard.timeanalyzer.categories;
|
||||
|
||||
import com.jdbernard.timeanalyzer.events.Event;
|
||||
|
||||
public interface CategoryFilter {
|
||||
public boolean matchesEvent(Event event);
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.jdbernard.timeanalyzer.categories
|
||||
|
||||
import com.jdbernard.timeanalyzer.events.Event
|
||||
|
||||
public class DescriptionBasedCategory extends Category {
|
||||
|
||||
public DescriptionBasedCategory(String description) {
|
||||
super()
|
||||
this.description = description.replaceAll(/\p{Punct}/, '') }
|
||||
|
||||
public boolean matchesEvent(Event e) {
|
||||
return e.description.replaceAll(/\p{Punct}/, '').toLowerCase() ==
|
||||
description.toLowerCase() }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.jdbernard.timeanalyzer.categories
|
||||
|
||||
import com.jdbernard.timeanalyzer.events.Event
|
||||
|
||||
public class FilteredCategory extends Category {
|
||||
|
||||
List<CategoryFilter> filters = []
|
||||
|
||||
public FilteredCategory(String description) {
|
||||
super(description) }
|
||||
|
||||
public boolean matchesEvent(Event e) {
|
||||
return filters.every { filter -> filter.matchesEvent(e) } }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.jdbernard.timeanalyzer.categories
|
||||
|
||||
import com.jdbernard.timeanalyzer.events.Event
|
||||
|
||||
public class GeneralCategory extends Category {
|
||||
|
||||
public GeneralCategory() { this("General") }
|
||||
|
||||
public GeneralCategory(String description) { super(description) }
|
||||
|
||||
public boolean matchesEvent(Event e) { true }
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.jdbernard.timeanalyzer.categories
|
||||
|
||||
import com.jdbernard.timeanalyzer.events.Event
|
||||
|
||||
import org.joda.time.Interval
|
||||
|
||||
public class TimeIntervalCategory extends Category {
|
||||
|
||||
private final Interval interval
|
||||
|
||||
public TimeIntervalCategory(String desc, Interval interval) {
|
||||
super(desc)
|
||||
this.interval = interval }
|
||||
|
||||
public boolean matchesEvent(Event e) {
|
||||
Interval eventIv = new Interval(e.start, e.duration)
|
||||
return interval.contains(eventIv) }
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.jdbernard.timeanalyzer.categories
|
||||
|
||||
import com.jdbernard.timeanalyzer.events.Event
|
||||
|
||||
import org.joda.time.Interval
|
||||
import org.joda.time.ReadableInstant
|
||||
import org.joda.time.ReadableInterval
|
||||
|
||||
public class TimeIntervalCategoryFilter implements CategoryFilter {
|
||||
|
||||
ReadableInterval interval
|
||||
|
||||
public TimeIntervalCategoryFilter(ReadableInterval interval) {
|
||||
this.interval = interval }
|
||||
|
||||
public TimeIntervalCategoryFilter(ReadableInstant start,
|
||||
ReadableInstant end) {
|
||||
this.interval = new Interval(start, end) }
|
||||
|
||||
public boolean matchesEvent(Event event) {
|
||||
return interval.contains(event.start) }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.jdbernard.timeanalyzer.categories
|
||||
|
||||
import com.jdbernard.timeanalyzer.events.Event
|
||||
|
||||
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 Event addEvent(Event e) {
|
||||
def m = e.description =~ descriptionPattern
|
||||
|
||||
e = new Event(e, description: m[0][1])
|
||||
|
||||
super.addEvent(e) }
|
||||
}
|
||||
+24
@@ -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)
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
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 extends CategorizationPlan {
|
||||
|
||||
public DailyCategorizationPlan() {}
|
||||
public DailyCategorizationPlan(Closure newCatSetupFun) {
|
||||
super(newCatSetupFun) }
|
||||
|
||||
boolean deservesNewCategory(Event event, List<Event> existingEvents) {
|
||||
Interval fullDay = new Interval(
|
||||
event.start.toDateMidnight(), Period.days(1))
|
||||
Interval eventIv = new Interval(
|
||||
event.start, event.duration)
|
||||
|
||||
return fullDay.contains(eventIv) }
|
||||
|
||||
Category newCategory(Event event, List<Event> existingEvents) {
|
||||
Interval fullday = new Interval(
|
||||
event.start.toDateMidnight(), Period.days(1))
|
||||
|
||||
Category newCat = new TimeIntervalCategory(
|
||||
event.start.toString("EEE, MMM dd"), fullday)
|
||||
|
||||
setupNewCategory(newCat)
|
||||
|
||||
return newCat }
|
||||
|
||||
List<Event> findEventsToRecategorize(Event event,
|
||||
List<Event> existingEvents) {
|
||||
Interval fullday = new Interval(
|
||||
event.start.toDateMidnight(), Period.days(1))
|
||||
|
||||
return existingEvents.findAll {
|
||||
Interval iv = new Interval(it.start, it.duration)
|
||||
fullday.contains(iv) } }
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.jdbernard.timeanalyzer.categorizationplans
|
||||
|
||||
import com.jdbernard.timeanalyzer.categories.Category
|
||||
import com.jdbernard.timeanalyzer.categories.DescriptionBasedCategory
|
||||
import com.jdbernard.timeanalyzer.events.Event
|
||||
|
||||
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 } }
|
||||
|
||||
public Category newCategory(Event event,
|
||||
List<Event> existingEvents) {
|
||||
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 } }
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.jdbernard.timeanalyzer.categorizationplans
|
||||
|
||||
import com.jdbernard.timeanalyzer.categories.Category
|
||||
import com.jdbernard.timeanalyzer.categories.TwoLevelCategory
|
||||
import com.jdbernard.timeanalyzer.events.Event
|
||||
|
||||
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 }
|
||||
|
||||
public Category newCategory(Event event, List<Event> el) {
|
||||
def m = event.description =~ TWO_LEVEL_PATTERN
|
||||
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]}:.*/ } }
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.jdbernard.timeanalyzer.chart
|
||||
|
||||
import com.jdbernard.timeanalyzer.categories.Category
|
||||
import org.jfree.data.general.DefaultPieDataset
|
||||
import org.jfree.data.general.PieDataset
|
||||
import org.jfree.util.SortOrder
|
||||
|
||||
public class Util {
|
||||
|
||||
public static PieDataset makePieDataset(Category category) {
|
||||
DefaultPieDataset dpds = new DefaultPieDataset()
|
||||
category.categories.each { cat ->
|
||||
dpds.setValue(cat.description, cat.duration.standardSeconds) }
|
||||
category.events.each { entry ->
|
||||
dpds.setValue(entry.description, entry.duration.standardSeconds) }
|
||||
dpds.sortByValues(SortOrder.DESCENDING)
|
||||
return dpds }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.jdbernard.timeanalyzer.events
|
||||
|
||||
import org.joda.time.ReadableDateTime
|
||||
import org.joda.time.Duration
|
||||
import org.joda.time.format.PeriodFormat
|
||||
import org.joda.time.format.PeriodFormatter
|
||||
|
||||
public class Event implements Cloneable {
|
||||
|
||||
public static PeriodFormatter periodFormatter = PeriodFormat.getDefault()
|
||||
|
||||
public final String description
|
||||
public final String notes
|
||||
public final ReadableDateTime start
|
||||
public Duration duration // should be final, allows modification for the
|
||||
// TimelineEventProcessor
|
||||
|
||||
public Event(String desc, String notes, ReadableDateTime start, Duration duration) {
|
||||
this.description = desc
|
||||
this.notes = notes
|
||||
this.start = start
|
||||
this.duration = duration }
|
||||
|
||||
public Event(Map params) {
|
||||
this.description = params.description ?: ""
|
||||
this.notes = params.notes ?: ""
|
||||
this.start = params.start
|
||||
this.duration = params.duration }
|
||||
|
||||
public Event(Map params, Event e) {
|
||||
this.description = params.description ?: e.description
|
||||
this.notes = params.notes ?: e.notes
|
||||
this.start = params.start ?: e.start
|
||||
this.duration = params.duration ?: e.duration }
|
||||
|
||||
public String toString() {
|
||||
String period = periodFormatter.print(this.duration.toPeriod())
|
||||
return "${description} (${period})" }
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.jdbernard.timeanalyzer.processors
|
||||
|
||||
import com.jdbernard.timeanalyzer.events.Event
|
||||
import com.jdbernard.timestamper.core.Timeline
|
||||
import com.jdbernard.timestamper.core.TimelineProperties
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.Duration
|
||||
|
||||
public class TimelineEventProcessor {
|
||||
|
||||
/** Events whose description matches one of these regex strings or
|
||||
* patterns are ignored. */
|
||||
List exclusions = []
|
||||
|
||||
public TimelineEventProcessor() {}
|
||||
|
||||
public TimelineEventProcessor(List exclusions) { this.exclusions = exclusions }
|
||||
|
||||
public List<Event> process(File timelinePropFile) {
|
||||
def timelineProps = new TimelineProperties(timelinePropFile)
|
||||
return process(timelineProps.timeline) }
|
||||
|
||||
public List<Event> process(Timeline timeline) {
|
||||
List<Event> events = []
|
||||
|
||||
timeline.each { marker ->
|
||||
Event e = new Event(
|
||||
description: marker.mark,
|
||||
notes: marker.notes,
|
||||
start: new DateTime(marker.timestamp),
|
||||
duration: new Duration(0))
|
||||
|
||||
println e
|
||||
|
||||
// if this is not the first event, then we need to update the
|
||||
// duration of the previous event
|
||||
if (events.size > 0) {
|
||||
Event lastEvent = events[-1]
|
||||
lastEvent.duration = new Duration(lastEvent.start, e.start) }
|
||||
|
||||
events << e }
|
||||
|
||||
def excluded = events.findAll { event ->
|
||||
exclusions.any { exclusion -> event.description ==~ exclusion } }
|
||||
|
||||
return events - excluded }
|
||||
|
||||
public static List<Event> process(def timeline, List exclusions) {
|
||||
def inst = new TimelineEventProcessor(exclusions)
|
||||
return inst.process(timeline) }
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.quantumdigital.ithelp.timeanalyzer
|
||||
|
||||
import com.jdbernard.timeanalyzer.categories.Category
|
||||
import com.jdbernard.timeanalyzer.categorizationplans.CategorizationPlan
|
||||
import com.jdbernard.timeanalyzer.events.Event
|
||||
|
||||
public class TicketCategorizationPlan extends CategorizationPlan {
|
||||
|
||||
private static def TICKET_PATTERN = ~/.*#(\d+).*/
|
||||
|
||||
public boolean deservesNewCategory(Event e, List<Event> el) {
|
||||
return e.description ==~ TICKET_PATTERN
|
||||
}
|
||||
|
||||
public Category newCategory(Event e, List<Event> el) {
|
||||
def m = e.description =~ TICKET_PATTERN
|
||||
|
||||
def newCat = new TicketCategory(m[0][1] as int)
|
||||
|
||||
setupNewCategory(newCat)
|
||||
|
||||
return newCat
|
||||
}
|
||||
|
||||
public List<Event> findEventsToRecategorize(Event e,
|
||||
List<Event> existingEvents) {
|
||||
def m = e.description =~ TICKET_PATTERN
|
||||
int ticketId = m[0][1] as int
|
||||
|
||||
return existingEvents.findAll {
|
||||
it.description ==~ /.*#${ticketId}.*/ }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.quantumdigital.ithelp.timeanalyzer
|
||||
|
||||
import com.jdbernard.timeanalyzer.categories.Category
|
||||
import com.jdbernard.timeanalyzer.events.Event
|
||||
|
||||
public class TicketCategory extends Category {
|
||||
|
||||
public final int ticketId
|
||||
|
||||
public TicketCategory(int ticketId) {
|
||||
super()
|
||||
this.ticketId = ticketId
|
||||
this.description = "Ticket #${ticketId}"
|
||||
}
|
||||
|
||||
public boolean matchesEvent(Event e) {
|
||||
return (e.description ==~ /.*#${ticketId}.*/)
|
||||
}
|
||||
|
||||
public Event addEvent(Event e) {
|
||||
TicketEvent te = new TicketEvent(e)
|
||||
events << te
|
||||
return te
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.quantumdigital.ithelp.timeanalyzer
|
||||
|
||||
import com.jdbernard.timeanalyzer.events.Event
|
||||
|
||||
public class TicketEvent extends Event {
|
||||
|
||||
public final int id
|
||||
|
||||
public TicketEvent(String desc, String notes, String start, String duration) {
|
||||
|
||||
super(desc, notes, start, duration)
|
||||
|
||||
def m = desc =~ /.*#(\d+).*/
|
||||
this.id = m[0][1] as int
|
||||
}
|
||||
|
||||
public TicketEvent(Map params) {
|
||||
super(params)
|
||||
|
||||
def m = description =~ /.*#(\d+).*/
|
||||
this.id = m[0][1] as int
|
||||
}
|
||||
|
||||
public TicketEvent(Map params, Event e) {
|
||||
super(params, e)
|
||||
|
||||
def m = description =~ /.*#(\d+).*/
|
||||
this.id = m[0][1] as int
|
||||
}
|
||||
|
||||
public TicketEvent(Event e) {
|
||||
super([:], e)
|
||||
|
||||
def m = description =~ /.*#(\d+).*/
|
||||
this.id = m[0][1] as int
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user