Skip to content

Code History Script API

Dmitry Kandalov edited this page Feb 29, 2016 · 1 revision

Scripts are written in Groovy and can use any Groovy classes/methods (If you are not familiar with Groovy, Language Specification and Working with collections can be particularly useful.)

Script can contain one or more implementations of Analyzer interface or just code with implicit data, context variables and will be implicitly converted into Analyzer implementation.

import codehistoryminer.publicapi.analysis.Context
import codehistoryminer.publicapi.analysis.Analyzer

interface Analyzer<T> {
	T analyze(List data, Context context)
}

Input data

data parameter is a list of objects corresponding to a line in csv file. There is no super-type for data objects. Fields can be accessed on objects using column names from csv file. Note that existing attributes are modifiable and new fields can be assigned: o.newAttribute = 123.

Since input data is read from csv file (which doesn't store information about types), during deserialization there is an attempt to convert strings into more specific types. There are attempts to convert csv string value into Date, Time, Integer, Double. If non of the above works, value is assumed to be a String.

Note that Date and Time classes are located in codehistoryminer.publicapi.lang package. See their interfaces below.

Transforming data

Each object in input data represents a file change (saved during VCS grabbing). To make access to data more convenient, there is FileChange class which wraps data object adding some methods. Input data can be wrapped at once FileChange.wrap(data) or each object data.collect{ new FileChange(it) }. See APIs section below for list of methods available in FileChange class.

Output data

Value returned by the script of Analyzer implementation will be displayed by the plugin. If output is Collection of Map, FileChange or input data objects, the output will be converted into temporary csv file and opened in IDE. Otherwise, output is displayed as popup messages using toString() method on objects.

There are plans to include more types of output including visualizations.

Context object

There are several reasons to use Context object:

  • to log information from complex or long-running script, e.g. context.log("hello"). Logs are written to IDE log file, see Main menu -> Help -> Show Log action.
  • to cancel long-running scripts, e.g. context.throwIfCancelled(),
  • to report progress from long running scripts, e.g. context.progress.set(percentComplete)

Examples

The simplest script which shows popup message.

"Hello world"

Show the amount of rows in input data.

data.size()

Filter data by author.

data.findAll{
	it.author == "Kent Beck"
}

Wrap data as FileChange object and filter by file type

import codehistoryminer.publicapi.analysis.filechange.FileChange

data.collect{ FileChange.wrap(it) }
    .findAll{ it.fileExtension() == "txt" }

Find number of file modifications by author.

data.groupBy{ it.author }
	.collect{ [author: it.key, fileModificationCount: it.value.size()] }
	.sort{ -it.fileModificationCount }

Script API classes

package codehistoryminer.publicapi.analysis

class Context {
	final Progress progress = new Progress(100)
	void throwIfCancelled()
	void log(String message)
}
package codehistoryminer.publicapi.lang

class Progress {
    void setTotal(long total)
    void set(long value)
    void add(long value)
    void complete()
}
package codehistoryminer.publicapi.analysis.filechange

class FileChange implements DataWrapper, Cloneable {
	static FileChange wrap(data)
	static Collection<FileChange> wrap(Collection collection)
	FileChange(data)

	ChangeType getFileChangeType()
	void setFileChangeType(ChangeType changeType)

	Date getCommitDate()
	String fullFileName()
	String nonEmptyPackageName()
	String nonEmptyFileName()
	String fileExtension()

	@Override getData()
	@Override FileChange clone()

	static List<FileChange> useLatestNameForMovedFiles(List<FileChange> events, Context context = Context.none)
}

enum ChangeType {
	ADDED,
	MODIFIED,
	DELETED,
	MOVED
}
package codehistoryminer.publicapi.lang;

class Date implements Comparable<Date> {
    Date(Time time)
    Date(java.util.Date javaDate, TimeZone timeZone = TimeZone.getDefault())
    static Date today(TimeZone timeZone = TimeZone.getDefault())
    static Date zero(TimeZone timeZone = TimeZone.getDefault())

    TimeZone timeZone()
    Date withTimeZone(TimeZone timeZone)
    java.util.Date javaDate()

    boolean after(Date that)
    boolean afterOrEqual(Date that)
    boolean before(Date that)
    boolean beforeOrEqual(Date that)

    Date shiftDays(int value)
    Date shift(int value, int calendarField)
    long distance(Date date)

    String format(Formatter formatter)

    @Override int compareTo(@NotNull Date that)
    @Override boolean equals(Object o)
    @Override int hashCode()

    static class Formatter {
        static final Formatter ddMMyyyy = new Formatter("ddMMyyyy");
        static final Formatter dd_MM_yyyy = new Formatter("dd/MM/yyyy");
        static final Formatter d__MMM__yyyy = new Formatter("d MMM yyyy");
        static final Formatter dd__MMM__yyyy = new Formatter("dd MMM yyyy");
        static final Formatter ISO1806 = new Formatter("yyyy-MM-dd'T'HH:mm:ssZ");
	}
}
package codehistoryminer.publicapi.lang;

class Time implements Comparable<Time> {
    Time(java.util.Date javaDate, TimeZone timeZone = TimeZone.getDefault())
    static Time now(TimeZone timeZone = TimeZone.getDefault())
    static Time zero(TimeZone timeZone = TimeZone.getDefault())
    Time withTimeZone(TimeZone timeZone)

    TimeZone timeZone()
    long millis()
    java.util.Date javaDate()
    Date toDate()

    Time floorToDay()
    Time floorToWeek()
    Time floorToMonth()
    Time floorTo(Interval interval)
    Time shift(int value, int calendarField)

    int get(int calendarField)
    int dayOfWeek()
    int hour()
    int minute()
  	Collection<Time> timeRange(Time toTime, Time.Interval interval)

    boolean after(Time time)
    boolean before(Time time)
    boolean beforeOrEqual(Time that)
    boolean afterOrEqual(Time time)

    String format(Formatter formatter)

	@Override int compareTo(@NotNull Time that)
    @Override String toString()
    @Override boolean equals(Object o)
    @Override int hashCode()

    static class Formatter {
        static final Formatter dd_MM_yyyy = new Formatter("dd/MM/yyyy");
        static final Formatter kkmm_dd_MM_yyyy = new Formatter("kk:mm dd/MM/yyyy");
        static final Formatter kkmmss_dd_MM_yyyy = new Formatter("kk:mm:ss dd/MM/yyyy");
        static final Formatter kkmmsss_dd_MM_yyyy = new Formatter("kk:mm:ss.SSS dd/MM/yyyy");
        static final Formatter yyyy_MM_dd_HHmmss_Z = new Formatter("yyyy-MM-dd HH:mm:ss Z");
        static final Formatter ISO1806 = new Formatter("yyyy-MM-dd'T'HH:mm:ssZ");
    }
Clone this wiki locally