From c2aef21306644a5d94f73fcb442f19a8f6185dc1 Mon Sep 17 00:00:00 2001
From: Eike Cochu <eike@cochu.com>
Date: Mon, 7 Mar 2016 23:43:05 +0100
Subject: [PATCH] added window resolutions, window resource

fixed quarterly window resolution
removed -ly from resolutions
added window resource
import process now creates windows
sequences embed windows
---
 .../de/vipra/rest/resource/InfoResource.java  |  5 -
 .../vipra/rest/resource/SearchResource.java   |  4 -
 .../vipra/rest/resource/SequenceResource.java |  8 --
 .../de/vipra/rest/resource/TopicResource.java |  8 --
 .../vipra/rest/resource/WindowResource.java   | 48 ++++++++++
 .../de/vipra/rest/resource/WordResource.java  |  4 -
 .../java/de/vipra/cmd/lda/DTMAnalyzer.java    | 21 ++++-
 .../java/de/vipra/util/CalendarUtils.java     |  2 +-
 .../main/java/de/vipra/util/Constants.java    | 91 ++++++++-----------
 .../main/java/de/vipra/util/StringUtils.java  | 18 +++-
 .../java/de/vipra/util/model/Sequence.java    | 38 +++-----
 .../de/vipra/util/model/SequenceFull.java     | 38 +++-----
 .../java/de/vipra/util/model/TopicFull.java   |  6 +-
 .../main/java/de/vipra/util/model/Window.java | 63 +++++++++++++
 14 files changed, 203 insertions(+), 151 deletions(-)
 create mode 100644 vipra-backend/src/main/java/de/vipra/rest/resource/WindowResource.java
 create mode 100644 vipra-util/src/main/java/de/vipra/util/model/Window.java

diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java
index a9f0de4f..9f1b8025 100644
--- a/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java
+++ b/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java
@@ -6,10 +6,8 @@ import java.lang.management.RuntimeMXBean;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
-import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
 
 import org.bson.types.ObjectId;
 
@@ -27,9 +25,6 @@ import de.vipra.util.service.MongoService;
 @Path("info")
 public class InfoResource {
 
-	@Context
-	UriInfo uri;
-
 	@GET
 	@Produces(MediaType.APPLICATION_JSON)
 	public Response getInfo() {
diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/SearchResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/SearchResource.java
index ab3f0f2e..bd0d0f63 100644
--- a/vipra-backend/src/main/java/de/vipra/rest/resource/SearchResource.java
+++ b/vipra-backend/src/main/java/de/vipra/rest/resource/SearchResource.java
@@ -16,7 +16,6 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
 
 import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.client.transport.TransportClient;
@@ -36,9 +35,6 @@ import de.vipra.util.model.ArticleFull;
 @Path("search")
 public class SearchResource {
 
-	@Context
-	UriInfo uri;
-
 	final TransportClient client;
 
 	public SearchResource(@Context final ServletContext servletContext) throws ConfigException, IOException {
diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/SequenceResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/SequenceResource.java
index 55dffedf..170570f7 100644
--- a/vipra-backend/src/main/java/de/vipra/rest/resource/SequenceResource.java
+++ b/vipra-backend/src/main/java/de/vipra/rest/resource/SequenceResource.java
@@ -13,10 +13,7 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
 import org.bson.types.ObjectId;
 
 import de.vipra.rest.Messages;
@@ -32,13 +29,8 @@ import de.vipra.util.service.MongoService;
 @Path("sequences")
 public class SequenceResource {
 
-	@Context
-	UriInfo uri;
-
 	final MongoService<SequenceFull, ObjectId> dbSequences;
 
-	public static final Logger log = LogManager.getLogger(SequenceResource.class);
-
 	public SequenceResource(@Context final ServletContext servletContext) throws ConfigException, IOException {
 		final Config config = Config.getConfig();
 		dbSequences = MongoService.getDatabaseService(config, SequenceFull.class);
diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java
index 4016c928..5037e99e 100644
--- a/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java
+++ b/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java
@@ -15,10 +15,7 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
 import org.bson.types.ObjectId;
 
 import de.vipra.rest.Messages;
@@ -38,14 +35,9 @@ import de.vipra.util.service.Service.QueryBuilder;
 @Path("topics")
 public class TopicResource {
 
-	@Context
-	UriInfo uri;
-
 	final MongoService<TopicFull, ObjectId> dbTopics;
 	final MongoService<ArticleFull, ObjectId> dbArticles;
 
-	public static final Logger log = LogManager.getLogger(TopicResource.class);
-
 	public TopicResource(@Context final ServletContext servletContext) throws ConfigException, IOException {
 		final Config config = Config.getConfig();
 		dbTopics = MongoService.getDatabaseService(config, TopicFull.class);
diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/WindowResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/WindowResource.java
new file mode 100644
index 00000000..bf3b922f
--- /dev/null
+++ b/vipra-backend/src/main/java/de/vipra/rest/resource/WindowResource.java
@@ -0,0 +1,48 @@
+package de.vipra.rest.resource;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import de.vipra.rest.model.APIError;
+import de.vipra.rest.model.ResponseWrapper;
+import de.vipra.util.Config;
+import de.vipra.util.ex.ConfigException;
+import de.vipra.util.model.Window;
+import de.vipra.util.service.MongoService;
+
+@Path("windows")
+public class WindowResource {
+
+	final MongoService<Window, Integer> dbWindows;
+
+	public WindowResource(@Context final ServletContext servletContext) throws ConfigException, IOException {
+		final Config config = Config.getConfig();
+		dbWindows = MongoService.getDatabaseService(config, Window.class);
+	}
+
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response getWindows() {
+		final ResponseWrapper<List<Window>> res = new ResponseWrapper<>();
+
+		if (res.hasErrors())
+			return Response.status(Response.Status.BAD_REQUEST).entity(res).build();
+
+		try {
+			final List<Window> windows = dbWindows.getAll();
+			return res.ok(windows);
+		} catch (final Exception e) {
+			e.printStackTrace();
+			res.addError(new APIError(Response.Status.BAD_REQUEST, "Error", e.getMessage()));
+			return Response.status(Response.Status.BAD_REQUEST).entity(res).build();
+		}
+	}
+}
\ No newline at end of file
diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java
index d45e45f8..d08b101c 100644
--- a/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java
+++ b/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java
@@ -14,7 +14,6 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
 
 import org.bson.types.ObjectId;
 
@@ -32,9 +31,6 @@ import de.vipra.util.service.Service.QueryBuilder;
 @Path("words")
 public class WordResource {
 
-	@Context
-	UriInfo uri;
-
 	final MongoService<Word, String> dbWords;
 	final MongoService<TopicFull, ObjectId> dbTopics;
 
diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/lda/DTMAnalyzer.java b/vipra-cmd/src/main/java/de/vipra/cmd/lda/DTMAnalyzer.java
index 88eb488d..4e4add26 100644
--- a/vipra-cmd/src/main/java/de/vipra/cmd/lda/DTMAnalyzer.java
+++ b/vipra-cmd/src/main/java/de/vipra/cmd/lda/DTMAnalyzer.java
@@ -34,6 +34,7 @@ import de.vipra.util.model.Topic;
 import de.vipra.util.model.TopicFull;
 import de.vipra.util.model.TopicRef;
 import de.vipra.util.model.TopicWord;
+import de.vipra.util.model.Window;
 import de.vipra.util.model.Word;
 import de.vipra.util.service.MongoService;
 
@@ -54,6 +55,7 @@ public class DTMAnalyzer extends Analyzer {
 	private MongoService<TopicFull, ObjectId> dbTopics;
 	private MongoService<SequenceFull, ObjectId> dbSequences;
 	private MongoService<Word, String> dbWords;
+	private MongoService<Window, Integer> dbWindows;
 
 	protected DTMAnalyzer() {
 		super("Dynamic Topic Model Analyzer");
@@ -73,6 +75,7 @@ public class DTMAnalyzer extends Analyzer {
 			dbTopics = MongoService.getDatabaseService(config, TopicFull.class);
 			dbSequences = MongoService.getDatabaseService(config, SequenceFull.class);
 			dbWords = MongoService.getDatabaseService(config, Word.class);
+			dbWindows = MongoService.getDatabaseService(config, Window.class);
 		} catch (ConfigException | IOException | ParseException e) {
 			throw new AnalyzerException(e);
 		}
@@ -168,10 +171,9 @@ public class DTMAnalyzer extends Analyzer {
 
 			// read topic definition files and create topics
 
+			final List<Window> newWindows = new ArrayList<>(sequencesCount);
 			final List<SequenceFull> newSequences = new ArrayList<>(Constants.K_TOPICS * sequencesCount);
-			// collects created topics
 			final List<TopicFull> newTopics = new ArrayList<>(Constants.K_TOPICS);
-			// collects created words
 			final Set<Word> newWords = new HashSet<>(wordCount);
 
 			log.info("vocabulary size: " + wordCount);
@@ -180,6 +182,15 @@ public class DTMAnalyzer extends Analyzer {
 
 			final boolean seqRelativeCutoff = Constants.MINIMUM_RELATIVE_PROB > 0;
 
+			// create sequence windows
+			for (int idxSeq = 0; idxSeq < sequencesCount; idxSeq++) {
+				final Window newWindow = new Window();
+				newWindow.setId(idxSeq);
+				newWindow.setStartDate(seqindex.getStartDate(idxSeq));
+				newWindow.setEndDate(seqindex.getEndDate(idxSeq));
+				newWindows.add(newWindow);
+			}
+
 			// for each topic
 			for (int idxTopic = 0; idxTopic < Constants.K_TOPICS; idxTopic++) {
 				final File seqFile = new File(outDirSeq,
@@ -252,10 +263,8 @@ public class DTMAnalyzer extends Analyzer {
 
 					// create sequence
 					final SequenceFull newSequenceFull = new SequenceFull();
-					newSequenceFull.setNumber(idxSeq);
+					newSequenceFull.setWindow(newWindows.get(idxSeq));
 					newSequenceFull.setWords(newSeqTopicWords);
-					newSequenceFull.setStartDate(seqindex.getStartDate(idxSeq));
-					newSequenceFull.setEndDate(seqindex.getEndDate(idxSeq));
 					newSequenceFull.setRelevance(relevance);
 					newSequenceFull.setTopic(new Topic(newTopic.getId()));
 					newSequences.add(newSequenceFull);
@@ -283,10 +292,12 @@ public class DTMAnalyzer extends Analyzer {
 			}
 
 			// recreate topics and words
+			dbWindows.drop();
 			dbSequences.drop();
 			dbTopics.drop();
 			dbWords.drop();
 			try {
+				dbWindows.createMultiple(newWindows);
 				dbSequences.createMultiple(newSequences);
 				dbTopics.createMultiple(newTopics);
 				dbWords.createMultiple(newWords);
diff --git a/vipra-util/src/main/java/de/vipra/util/CalendarUtils.java b/vipra-util/src/main/java/de/vipra/util/CalendarUtils.java
index 1d8df008..3539e91a 100644
--- a/vipra-util/src/main/java/de/vipra/util/CalendarUtils.java
+++ b/vipra-util/src/main/java/de/vipra/util/CalendarUtils.java
@@ -9,7 +9,7 @@ public class CalendarUtils {
 
 	/**
 	 * Returns the quarter of the passed calendar. Months are turned into
-	 * quarters of 4: Jan-Mar: 1, Apr-Jun: 2, Jul-Sep: 3, Oct:Dec: 4.
+	 * quarters of 4: Jan-Mar: 0, Apr-Jun: 1, Jul-Sep: 2, Oct:Dec: 3.
 	 *
 	 * @param c
 	 *            the calendar to be used
diff --git a/vipra-util/src/main/java/de/vipra/util/Constants.java b/vipra-util/src/main/java/de/vipra/util/Constants.java
index 9dd6e9a6..e7a1d30b 100644
--- a/vipra-util/src/main/java/de/vipra/util/Constants.java
+++ b/vipra-util/src/main/java/de/vipra/util/Constants.java
@@ -129,7 +129,7 @@ public class Constants {
 	 * find a list of available analyzers,
 	 * {@link de.vipra.util.Constants.WindowResolution}.
 	 */
-	public static final WindowResolution WINDOW_RESOLUTION = WindowResolution.YEARLY;
+	public static final WindowResolution WINDOW_RESOLUTION = WindowResolution.YEAR;
 
 	/**
 	 * Stopwords list. Extensive list of stopwords used to clean imported
@@ -337,9 +337,13 @@ public class Constants {
 	 * Describes the window size, when using dynamic topic modeling
 	 */
 	public static enum WindowResolution {
-		MONTHLY("monthly"),
-		QUARTERLY("quarterly"),
-		YEARLY("yearly");
+		YEAR("year"),
+		QUARTER("quarter"),
+		MONTH("month"),
+		DAY("day"),
+		HOUR("hour"),
+		MINUTE("minute"),
+		SECOND("second");
 
 		public final String name;
 
@@ -351,46 +355,28 @@ public class Constants {
 			name = def.name;
 		}
 
-		public String sequenceLabel(final Date date) {
-			final Calendar c = new GregorianCalendar();
-			c.setTime(date);
-			String str = c.get(Calendar.YEAR) + "";
-			switch (this) {
-				case QUARTERLY:
-					str += "-" + CalendarUtils.getQuarter(c);
-					break;
-				case MONTHLY:
-					final int month = c.get(Calendar.MONTH);
-					str += "-" + (month < 10 ? "0" : "") + month;
-					break;
-				default:
-					break;
-			}
-			return str;
-		}
-
 		public Date startDate(final Date date) {
 			final Calendar in = new GregorianCalendar();
 			in.setTime(date);
 			final Calendar out = new GregorianCalendar();
-			int month = 0;
+			out.setTime(date);
+			out.set(Calendar.MILLISECOND, 0);
 			switch (this) {
-				case QUARTERLY:
-					month = CalendarUtils.getQuarterStart(in);
-					break;
-				case MONTHLY:
-					month = in.get(Calendar.MONTH);
-					break;
+				case YEAR:
+					out.set(Calendar.MONTH, in.getActualMinimum(Calendar.MONTH));
+				case MONTH:
+					out.set(Calendar.DAY_OF_MONTH, in.getActualMinimum(Calendar.DAY_OF_MONTH));
+				case DAY:
+					out.set(Calendar.HOUR, in.getActualMinimum(Calendar.HOUR));
+				case HOUR:
+					out.set(Calendar.MINUTE, in.getActualMinimum(Calendar.MINUTE));
+				case MINUTE:
+					out.set(Calendar.SECOND, in.getActualMinimum(Calendar.SECOND));
 				default:
 					break;
 			}
-			out.set(Calendar.YEAR, in.get(Calendar.YEAR));
-			out.set(Calendar.MONTH, month);
-			out.set(Calendar.DAY_OF_MONTH, out.getActualMaximum(Calendar.DAY_OF_MONTH));
-			out.set(Calendar.HOUR, 0);
-			out.set(Calendar.MINUTE, 0);
-			out.set(Calendar.SECOND, 0);
-			out.set(Calendar.MILLISECOND, 0);
+			if (this == QUARTER)
+				out.set(Calendar.MONTH, CalendarUtils.getQuarter(in) * 3);
 			return out.getTime();
 		}
 
@@ -398,32 +384,29 @@ public class Constants {
 			final Calendar in = new GregorianCalendar();
 			in.setTime(date);
 			final Calendar out = new GregorianCalendar();
-			int month = 0;
+			out.setTime(date);
+			out.set(Calendar.MILLISECOND, 0);
 			switch (this) {
-				case YEARLY:
-					month = 11;
-					break;
-				case QUARTERLY:
-					month = CalendarUtils.getQuarterEnd(in);
-					break;
-				case MONTHLY:
-					month = in.get(Calendar.MONTH);
-					break;
+				case YEAR:
+					out.set(Calendar.MONTH, out.getActualMaximum(Calendar.MONTH));
+				case MONTH:
+					out.set(Calendar.DAY_OF_MONTH, out.getActualMaximum(Calendar.DAY_OF_MONTH));
+				case DAY:
+					out.set(Calendar.HOUR, out.getActualMaximum(Calendar.HOUR));
+				case HOUR:
+					out.set(Calendar.MINUTE, out.getActualMaximum(Calendar.MINUTE));
+				case MINUTE:
+					out.set(Calendar.SECOND, out.getActualMaximum(Calendar.SECOND));
 				default:
 					break;
 			}
-			out.set(Calendar.YEAR, in.get(Calendar.YEAR));
-			out.set(Calendar.MONTH, month);
-			out.set(Calendar.DAY_OF_MONTH, out.getActualMaximum(Calendar.DAY_OF_MONTH));
-			out.set(Calendar.HOUR, 0);
-			out.set(Calendar.MINUTE, 0);
-			out.set(Calendar.SECOND, 0);
-			out.set(Calendar.MILLISECOND, 0);
+			if (this == QUARTER)
+				out.set(Calendar.MONTH, (CalendarUtils.getQuarter(in) * 3) + 2);
 			return out.getTime();
 		}
 
 		public static WindowResolution DEFAULT() {
-			return YEARLY;
+			return YEAR;
 		}
 
 		public static WindowResolution fromString(final String text) {
diff --git a/vipra-util/src/main/java/de/vipra/util/StringUtils.java b/vipra-util/src/main/java/de/vipra/util/StringUtils.java
index 442678da..295b1dde 100644
--- a/vipra-util/src/main/java/de/vipra/util/StringUtils.java
+++ b/vipra-util/src/main/java/de/vipra/util/StringUtils.java
@@ -18,7 +18,7 @@ public class StringUtils {
 		return input.substring(0, maxLength - ellip.length()).concat(ellip);
 	}
 
-	public static String join(final Iterable<String> it, final String separator) {
+	public static String join(final Iterable<String> it, final String separator, final boolean reverse) {
 		final Iterator<String> iter = it.iterator();
 		if (iter.hasNext()) {
 			final StringBuilder sb = new StringBuilder(iter.next());
@@ -31,15 +31,23 @@ public class StringUtils {
 	}
 
 	public static String join(final Iterable<String> it) {
-		return join(it, " ");
+		return join(it, " ", false);
 	}
 
-	public static String join(final String[] arr, final String separator) {
-		return join(Arrays.asList(arr), separator);
+	public static String join(final Iterable<String> it, final String separator) {
+		return join(it, separator, false);
 	}
 
 	public static String join(final String[] arr) {
-		return join(arr, " ");
+		return join(arr, " ", false);
+	}
+
+	public static String join(final String[] arr, final String separator) {
+		return join(arr, separator, false);
+	}
+
+	public static String join(final String[] arr, final String separator, final boolean reverse) {
+		return join(Arrays.asList(arr), separator, reverse);
 	}
 
 	public static String timeString(long nanos) {
diff --git a/vipra-util/src/main/java/de/vipra/util/model/Sequence.java b/vipra-util/src/main/java/de/vipra/util/model/Sequence.java
index edc82a45..770f5e61 100644
--- a/vipra-util/src/main/java/de/vipra/util/model/Sequence.java
+++ b/vipra-util/src/main/java/de/vipra/util/model/Sequence.java
@@ -1,9 +1,9 @@
 package de.vipra.util.model;
 
 import java.io.Serializable;
-import java.util.Date;
 
 import org.bson.types.ObjectId;
+import org.mongodb.morphia.annotations.Embedded;
 import org.mongodb.morphia.annotations.Entity;
 import org.mongodb.morphia.annotations.Id;
 
@@ -13,9 +13,10 @@ public class Sequence implements Model<ObjectId>, Comparable<Sequence>, Serializ
 
 	@Id
 	private ObjectId id = new ObjectId();
-	private Date startDate;
-	private Date endDate;
-	private Integer number;
+
+	@Embedded
+	private Window window;
+
 	private Double relevance;
 
 	public Sequence() {}
@@ -34,28 +35,12 @@ public class Sequence implements Model<ObjectId>, Comparable<Sequence>, Serializ
 		this.id = id;
 	}
 
-	public Date getStartDate() {
-		return startDate;
-	}
-
-	public void setStartDate(final Date startDate) {
-		this.startDate = startDate;
-	}
-
-	public Date getEndDate() {
-		return endDate;
-	}
-
-	public void setEndDate(final Date endDate) {
-		this.endDate = endDate;
-	}
-
-	public Integer getNumber() {
-		return number;
+	public Window getWindow() {
+		return window;
 	}
 
-	public void setNumber(final Integer number) {
-		this.number = number;
+	public void setWindow(final Window window) {
+		this.window = window;
 	}
 
 	public Double getRelevance() {
@@ -68,13 +53,12 @@ public class Sequence implements Model<ObjectId>, Comparable<Sequence>, Serializ
 
 	@Override
 	public int compareTo(final Sequence o) {
-		return number - o.getNumber();
+		return window.getId() - o.getWindow().getId();
 	}
 
 	@Override
 	public String toString() {
-		return "Sequence [id=" + id + ", startDate=" + startDate + ", endDate=" + endDate + ", number=" + number
-				+ ", relevance=" + relevance + "]";
+		return "Sequence [id=" + id + ", window=" + window + ", relevance=" + relevance + "]";
 	}
 
 }
diff --git a/vipra-util/src/main/java/de/vipra/util/model/SequenceFull.java b/vipra-util/src/main/java/de/vipra/util/model/SequenceFull.java
index 332e52f6..e35321bb 100644
--- a/vipra-util/src/main/java/de/vipra/util/model/SequenceFull.java
+++ b/vipra-util/src/main/java/de/vipra/util/model/SequenceFull.java
@@ -1,7 +1,6 @@
 package de.vipra.util.model;
 
 import java.io.Serializable;
-import java.util.Date;
 import java.util.List;
 
 import org.bson.types.ObjectId;
@@ -18,9 +17,10 @@ public class SequenceFull implements Model<ObjectId>, Comparable<SequenceFull>,
 
 	@Id
 	private ObjectId id = new ObjectId();
-	private Date startDate;
-	private Date endDate;
-	private Integer number;
+
+	@Embedded
+	private Window window;
+
 	private Double relevance;
 
 	@Reference
@@ -41,28 +41,12 @@ public class SequenceFull implements Model<ObjectId>, Comparable<SequenceFull>,
 		this.id = id;
 	}
 
-	public Date getStartDate() {
-		return startDate;
-	}
-
-	public void setStartDate(final Date startDate) {
-		this.startDate = startDate;
-	}
-
-	public Date getEndDate() {
-		return endDate;
-	}
-
-	public void setEndDate(final Date endDate) {
-		this.endDate = endDate;
-	}
-
-	public Integer getNumber() {
-		return number;
+	public Window getWindow() {
+		return window;
 	}
 
-	public void setNumber(final Integer number) {
-		this.number = number;
+	public void setWindow(final Window window) {
+		this.window = window;
 	}
 
 	public Double getRelevance() {
@@ -91,13 +75,13 @@ public class SequenceFull implements Model<ObjectId>, Comparable<SequenceFull>,
 
 	@Override
 	public int compareTo(final SequenceFull o) {
-		return number - o.getNumber();
+		return window.getId() - o.getWindow().getId();
 	}
 
 	@Override
 	public String toString() {
-		return "SequenceFull [id=" + id + ", startDate=" + startDate + ", endDate=" + endDate + ", number=" + number
-				+ ", relevance=" + relevance + ", topic=" + topic + ", words=" + words + "]";
+		return "SequenceFull [id=" + id + ", window=" + window + ", relevance=" + relevance + ", topic=" + topic
+				+ ", words=" + words + "]";
 	}
 
 }
diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java b/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java
index 07f6de37..ffbb2040 100644
--- a/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java
+++ b/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java
@@ -96,7 +96,7 @@ public class TopicFull implements Model<ObjectId>, Serializable {
 		return avgRelevance;
 	}
 
-	public void setAvgRelevance(Double avgRelevance) {
+	public void setAvgRelevance(final Double avgRelevance) {
 		this.avgRelevance = avgRelevance;
 	}
 
@@ -104,7 +104,7 @@ public class TopicFull implements Model<ObjectId>, Serializable {
 		return varRelevance;
 	}
 
-	public void setVarRelevance(Double varRelevance) {
+	public void setVarRelevance(final Double varRelevance) {
 		this.varRelevance = varRelevance;
 	}
 
@@ -112,7 +112,7 @@ public class TopicFull implements Model<ObjectId>, Serializable {
 		return dynamic;
 	}
 
-	public void setDynamic(boolean dynamic) {
+	public void setDynamic(final boolean dynamic) {
 		this.dynamic = dynamic;
 	}
 
diff --git a/vipra-util/src/main/java/de/vipra/util/model/Window.java b/vipra-util/src/main/java/de/vipra/util/model/Window.java
new file mode 100644
index 00000000..7e88315b
--- /dev/null
+++ b/vipra-util/src/main/java/de/vipra/util/model/Window.java
@@ -0,0 +1,63 @@
+package de.vipra.util.model;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import org.mongodb.morphia.annotations.Entity;
+import org.mongodb.morphia.annotations.Id;
+
+import de.vipra.util.Constants.WindowResolution;
+
+@SuppressWarnings("serial")
+@Entity(value = "windows", noClassnameStored = true)
+public class Window implements Model<Integer>, Serializable, Comparable<Window> {
+
+	@Id
+	private Integer id;
+
+	private Date startDate;
+
+	private Date endDate;
+
+	private WindowResolution windowResolution;
+
+	@Override
+	public Integer getId() {
+		return id;
+	}
+
+	@Override
+	public void setId(final Integer id) {
+		this.id = id;
+	}
+
+	public Date getStartDate() {
+		return startDate;
+	}
+
+	public void setStartDate(final Date startDate) {
+		this.startDate = startDate;
+	}
+
+	public Date getEndDate() {
+		return endDate;
+	}
+
+	public void setEndDate(final Date endDate) {
+		this.endDate = endDate;
+	}
+
+	public WindowResolution getWindowResolution() {
+		return windowResolution;
+	}
+
+	public void setWindowResolution(final WindowResolution windowResolution) {
+		this.windowResolution = windowResolution;
+	}
+
+	@Override
+	public int compareTo(final Window o) {
+		return id - o.getId();
+	}
+
+}
-- 
GitLab