Merge branch 'temp'
This commit is contained in:
commit
b6dc3eb98c
19
time-analyzer/build.xml
Normal file
19
time-analyzer/build.xml
Normal file
@ -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>
|
248
time-analyzer/jdb-build-1.9.xml
Normal file
248
time-analyzer/jdb-build-1.9.xml
Normal file
@ -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>
|
BIN
time-analyzer/lib/compile/jar/jcommon-1.0.16.jar
Normal file
BIN
time-analyzer/lib/compile/jar/jcommon-1.0.16.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/compile/jar/jfreechart-1.0.13-experimental.jar
Normal file
BIN
time-analyzer/lib/compile/jar/jfreechart-1.0.13-experimental.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/compile/jar/jfreechart-1.0.13-swt.jar
Normal file
BIN
time-analyzer/lib/compile/jar/jfreechart-1.0.13-swt.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/compile/jar/jfreechart-1.0.13.jar
Normal file
BIN
time-analyzer/lib/compile/jar/jfreechart-1.0.13.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/compile/jar/joda-time-1.6.2.jar
Normal file
BIN
time-analyzer/lib/compile/jar/joda-time-1.6.2.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/compile/jar/slf4j-api-1.6.1.jar
Normal file
BIN
time-analyzer/lib/compile/jar/slf4j-api-1.6.1.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/compile/jar/timestamper-2.0-lib.jar
Normal file
BIN
time-analyzer/lib/compile/jar/timestamper-2.0-lib.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/runtime/jar/iText-2.1.5.jar
Normal file
BIN
time-analyzer/lib/runtime/jar/iText-2.1.5.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/runtime/jar/jcommon-1.0.16.jar
Normal file
BIN
time-analyzer/lib/runtime/jar/jcommon-1.0.16.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/runtime/jar/jfreechart-1.0.13-experimental.jar
Normal file
BIN
time-analyzer/lib/runtime/jar/jfreechart-1.0.13-experimental.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/runtime/jar/jfreechart-1.0.13-swt.jar
Normal file
BIN
time-analyzer/lib/runtime/jar/jfreechart-1.0.13-swt.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/runtime/jar/jfreechart-1.0.13.jar
Normal file
BIN
time-analyzer/lib/runtime/jar/jfreechart-1.0.13.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/runtime/jar/joda-time-1.6.2.jar
Normal file
BIN
time-analyzer/lib/runtime/jar/joda-time-1.6.2.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/runtime/jar/logback-classic-0.9.26.jar
Normal file
BIN
time-analyzer/lib/runtime/jar/logback-classic-0.9.26.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/runtime/jar/logback-core-0.9.26.jar
Normal file
BIN
time-analyzer/lib/runtime/jar/logback-core-0.9.26.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/runtime/jar/slf4j-api-1.6.1.jar
Normal file
BIN
time-analyzer/lib/runtime/jar/slf4j-api-1.6.1.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/runtime/jar/swtgraphics2d.jar
Normal file
BIN
time-analyzer/lib/runtime/jar/swtgraphics2d.jar
Normal file
Binary file not shown.
BIN
time-analyzer/lib/runtime/jar/timestamper-2.0-lib.jar
Normal file
BIN
time-analyzer/lib/runtime/jar/timestamper-2.0-lib.jar
Normal file
Binary file not shown.
5
time-analyzer/project.properties
Normal file
5
time-analyzer/project.properties
Normal file
@ -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
time-analyzer/resources/main/jdbernard-timeline.txt
Executable file
BIN
time-analyzer/resources/main/jdbernard-timeline.txt
Executable file
Binary file not shown.
84
time-analyzer/resources/main/start-script.groovy
Normal file
84
time-analyzer/resources/main/start-script.groovy
Normal file
@ -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);
|
||||
}
|
@ -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 }
|
||||
|
||||
}
|
@ -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) }
|
||||
}
|
@ -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) }
|
||||
}
|
@ -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)
|
||||
}
|
@ -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) } }
|
||||
}
|
@ -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 } }
|
||||
|
||||
}
|
@ -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})" }
|
||||
|
||||
}
|
@ -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) }
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user