Merge branch 'temp'

This commit is contained in:
Jonathan Bernard 2024-08-04 21:23:13 -05:00
commit b6dc3eb98c
41 changed files with 921 additions and 0 deletions

19
time-analyzer/build.xml Normal file
View 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>

View 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>

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.

View 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

Binary file not shown.

View 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 }

View File

@ -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})" }
}

View File

@ -0,0 +1,7 @@
package com.jdbernard.timeanalyzer.categories;
import com.jdbernard.timeanalyzer.events.Event;
public interface CategoryFilter {
public boolean matchesEvent(Event event);
}

View File

@ -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() }
}

View File

@ -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) } }
}

View File

@ -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 }
}

View File

@ -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) }
}

View File

@ -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) }
}

View File

@ -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) }
}

View File

@ -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)
}

View File

@ -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) } }
}

View File

@ -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 } }
}

View File

@ -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]}:.*/ } }
}

View File

@ -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 }
}

View File

@ -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})" }
}

View File

@ -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) }
}

View File

@ -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}.*/ }
}
}

View File

@ -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
}
}

View File

@ -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
}
}