Finised initial implementation. Need to test.

This commit is contained in:
Jonathan Bernard 2011-01-10 00:24:08 -06:00
parent 9f4008a775
commit 68b51640af
12 changed files with 242 additions and 78 deletions

44
build.xml Normal file
View File

@ -0,0 +1,44 @@
<project name="Time Analyzer" default="compile">
<property environment="env"/>
<property file="project.properties"/>
<path id="groovy.lib">
<fileset dir="${env.GROOVY_HOME}/lib">
<include name="*.jar"/>
</fileset>
</path>
<path id="local.compile.lib">
<fileset dir="lib/compile">
<include name="*.jar"/>
</fileset>
</path>
<path id="local.runtime.lib">
<fileset dir="lib/runtime">
<include name="*.jar"/>
</fileset>
</path>
<taskdef name="groovyc" classpathref="groovy.lib"
classname="org.codehaus.groovy.ant.Groovyc"/>
<target name="clean">
<delete dir="build"/>
</target>
<target name="compile">
<mkdir dir="build/main"/>
<groovyc srcdir="src/main" destdir="build/main"
includeAntRuntime="false">
<classpath>
<path refid="groovy.lib"/>
<path refid="local.compile.lib"/>
</classpath>
<javac/>
</groovyc>
</target>
</project>

View File

@ -1,15 +1,79 @@
package com.jdbernard.timeanalyzer
public abstract class Category implements Comparable<T extends Category> {
import org.joda.time.Duration
/**
* A category represents a collection of like events.
*/
public abstract class Category implements Comparable<Category> {
List<Entry> entries
List<Category> categories
String description
public Category() {
entries = []
categories = []
description = "Unnamed Category"
}
public abstract boolean matchesEvent(Event e)
public abstract Entry addEvent(Event e)
public Category() { entries = [] }
public Entry addEvent(Event e) {
Entry entry
public int compareTo(C other) {
// see if we have a subcategory that will hold this event
entry = addToSubCategory(e)
if (!entry) {
// if not, do we have another entry that could be grouped with
// this one to create a new category, based on the description?
def existingEntry = entries.find
{ it.description == e.description }
// yes
if (existingEntry) {
// create the new category
category = new DescriptionBasedCategory(e.description)
// remove the existing entry from this category and
// add it to the subcategory
this.entries -= existingEntry
category.entries << existingEntry
// add the new event to the category
entry = category.addEvent(e)
// add the category to our list of subcategories
categories << category
}
// no, let's create a generic entry and add it
else {
entry = new Entry(this, e)
entries << entry
}
}
return entry
}
public Entry addToSubcategory(Event e) {
// find all matching subcategories
def matchingCategories = categories.findAll { it.matchesEvent(e) }
if (matchingCategories)
return matchingCategories[0].addEvent(e)
return null
}
public Duration getDuration() {
return categories.sum { it.duration } + entries.sum { it.duration }
}
public int compareTo(Category other) {
return this.getDuration().compareTo(other.getDuration())
}
}

View File

@ -0,0 +1,16 @@
package com.jdbernard.timeanalyzer
import org.joda.time.Duration
public class DescriptionBasedCategory extends Category {
public DescriptionBasedCategory(String description) {
super()
this.description = description
}
public boolean matchesEvent(Event e) {
return e.description == description
}
}

View File

@ -1,6 +1,32 @@
package com.jdbernard.timanalyzer;
package com.jdbernard.timeanalyzer;
public class Entry extends Event {
import org.joda.time.DateTime;
import org.joda.time.Duration;
public class Entry {
public String description;
public String notes;
public DateTime start;
public Duration duration;
public Category category;
public Entry(Category c, String description, DateTime start,
Duration duration) {
this(c, description, "", start, duration);
}
public Entry(Category c, Event e) {
this(c, e.description, e.notes, e.start, e.duration);
}
public Entry(Category c, String description, String notes, DateTime start,
Duration duration) {
this.description = description;
this.notes = notes;
this.start = start;
this.duration = duration;
this.category = category;
}
}

View File

@ -1,25 +0,0 @@
package com.jdbernard.timeanalyzer;
public abstract class EventTransformation {
private Category category;
public EventTransformation(Category cat) {
this.category = cat;
}
/**
* Determine if this transformation is applicable for a given event.
* @param e An event to match.
* @return *true* if this transformation is applicable to the given event,
* *false* otherwise.
*/
public abstract boolean matches(Event e);
/**
* Transform an entry.
* @param e The Event to transform.
* @return A new Entry.
*/
public abstract Entry transform(Event e);
}

View File

@ -1,21 +0,0 @@
package com.jdbernard.timeanalyzer
public class EventTransformer {
List transformations
public EventTransformer(List transformations) {
this.transformations = transformations
}
/**
* Transform an event. The first matching transformation is used.
* @param e The event to transform.
* @return The resulting Entry.
*/
public Entry transform(Event e) {
for (t : transformations)
if (t.matches(e))
return t.transform(t)
}
}

View File

@ -0,0 +1,12 @@
package com.jdbernard.timeanalyzer
public class GeneralCategory {
public GeneralCategory() {
super()
description = "General"
}
public boolean matchesEvent(Event e) { true }
}

View File

@ -1,15 +1,58 @@
package com.quantumdigital.ithelp.timeanalyzer
import com.jdbernard.timeanalyzer.Category
import com.jdbernard.timeanalyzer.Entry
import com.jdbernard.timeanalyzer.Event
public class ITHelpCategory extends Category {
private def ithelpPattern = ~/^ITHelp:(.*)$/
private def ticketPattern = ~/^ITHelp:.*#(\d+).*/
public boolean matchesEvent(Event e) {
return (e.description ==~ /^ITHelp:.*/)
return (e.description ==~ ithelpPattern)
}
public ITHelpEntry addEvent(Event e) {
assert eventMatches(e)
public Entry addEvent(Event e) {
Entry entry
// try to add to an existing category
entry = addToSubcategory(e)
// no existing category matches
if (!entry) {
// see if it is a new ticket entry
def ticketMatch = e.description =~ ticketPattern
// if is a new ticket
if(ticketMatch) {
// parse the ticket number
int ticketId = ticketMatch[0][1] as int
// create the category
TicketCategory category = new TicketCategory(ticketId)
// add the category to our list
categories << category
// add the event to the category
entry = category.addEvent(e)
}
// not a new ticket, use the general logic for adding
else {
// add a general entry to this category
entry = super.addEvent(e)
// adjust the description (remove the ITHelp tag)
def m = entry.description =~ ithelpPattern
entry.description = m[0][1]
}
}
return entry
}
}

View File

@ -1,5 +0,0 @@
package com.quantumdigital.ithelp.timeanalyzer
import com.jdbernard.timeanalyzer.Event
public class ITHelpEntry extends Entry

View File

@ -1,12 +0,0 @@
package com.quantumdigital.ithelp.timeanalyzer
import com.jdbernard.timeanalyzer.EventTransformation
public class ITHelpEventTransformation extends EventTransformation {
public boolean matches(Event e) {
return e.description ==~ /^ITHelp: .*/
}
public Entry
}

View File

@ -1,15 +1,20 @@
package com.quantumdigital.ithelp.timeanalyzer
import com.jdbernard.timeanalyzer.Category
import com.jdbernard.timeanalyzer.Event
public class TicketCategory extends Category {
public boolean matchesEvent(Event e) {
return (e.description ==~ /ITHelp:.*#\d+.*/)
public final int ticketId
public TicketCategory(int ticketId) {
super()
this.ticketId = ticketId
this.description = "Ticket #${ticketId}"
}
public TicketEntry addEvent(Event e) {
assert eventMatches(e)
public boolean matchesEvent(Event e) {
return (e.description ==~ /ITHelp:.*#${ticketId}.*/)
}
TicketEntry entry = new TicketEntry(e)
entries << entry
}

View File

@ -0,0 +1,17 @@
package com.quantumdigital.ithelp.timeanalyzer
import com.jdbernard.timeanalyzer.Entry
import com.jdbernard.timeanalyzer.Event
public class TicketEntry extends Entry {
public int id
public TicketEntry(ITHelpCategory category, Event e) {
super(category, e)
def m = e.description =~ /^ITHelp:(.*#(\d+).*)$/
this.description = m[0][1]
this.id = m[0][2] as int
}
}