diff --git a/sitestats/schema/db-tables/hsqldb/sitestats.sql b/sitestats/schema/db-tables/hsqldb/sitestats.sql index 6c33f2ae9b48..b1204f917857 100644 --- a/sitestats/schema/db-tables/hsqldb/sitestats.sql +++ b/sitestats/schema/db-tables/hsqldb/sitestats.sql @@ -1,7 +1,7 @@ create table SST_EVENTS (ID bigint generated by default as identity (start with 1), USER_ID varchar(99) not null, SITE_ID varchar(99) not null, EVENT_ID varchar(32) not null, EVENT_DATE date not null, EVENT_COUNT bigint not null, primary key (ID)); create table SST_JOB_RUN (ID bigint generated by default as identity (start with 1), JOB_START_DATE timestamp, JOB_END_DATE timestamp, START_EVENT_ID bigint, END_EVENT_ID bigint, LAST_EVENT_DATE timestamp, primary key (ID)); create table SST_PREFERENCES (ID bigint generated by default as identity (start with 1), SITE_ID varchar(99) not null, PREFS longvarchar not null, primary key (ID)); -create table SST_PRESENCES (ID bigint generated by default as identity (start with 1), SITE_ID varchar(99) not null, USER_ID varchar(99) not null, P_DATE date not null, DURATION bigint default 0 not null, LAST_VISIT_START_TIME timestamp default null, primary key (ID)); +create table SST_PRESENCES (ID bigint generated by default as identity (start with 1), SITE_ID varchar(99) not null, USER_ID varchar(99) not null, P_DATE date not null, DURATION bigint default 0 not null, LAST_VISIT_START_TIME timestamp default null, CURRENT_OPEN_SESSIONS int default 0 not null, primary key (ID)); create table SST_REPORTS (ID bigint generated by default as identity (start with 1), SITE_ID varchar(99), TITLE varchar(255) not null, DESCRIPTION longvarchar, HIDDEN bit, REPORT_DEF longvarchar not null, CREATED_BY varchar(99) not null, CREATED_ON timestamp not null, MODIFIED_BY varchar(99), MODIFIED_ON timestamp, primary key (ID)); create table SST_RESOURCES (ID bigint generated by default as identity (start with 1), USER_ID varchar(99) not null, SITE_ID varchar(99) not null, RESOURCE_REF varchar(255) not null, RESOURCE_ACTION varchar(12) not null, RESOURCE_DATE date not null, RESOURCE_COUNT bigint not null, primary key (ID)); create table SST_SITEACTIVITY (ID bigint generated by default as identity (start with 1), SITE_ID varchar(99) not null, ACTIVITY_DATE date not null, EVENT_ID varchar(32) not null, ACTIVITY_COUNT bigint not null, primary key (ID)); diff --git a/sitestats/schema/db-tables/mysql/sitestats.sql b/sitestats/schema/db-tables/mysql/sitestats.sql index 355585c071ba..73eeccdd21ec 100644 --- a/sitestats/schema/db-tables/mysql/sitestats.sql +++ b/sitestats/schema/db-tables/mysql/sitestats.sql @@ -1,7 +1,7 @@ create table SST_EVENTS (ID bigint not null auto_increment, USER_ID varchar(99) not null, SITE_ID varchar(99) not null, EVENT_ID varchar(32) not null, EVENT_DATE date not null, EVENT_COUNT bigint not null, primary key (ID)); create table SST_JOB_RUN (ID bigint not null auto_increment, JOB_START_DATE datetime, JOB_END_DATE datetime, START_EVENT_ID bigint, END_EVENT_ID bigint, LAST_EVENT_DATE datetime, primary key (ID)); create table SST_PREFERENCES (ID bigint not null auto_increment, SITE_ID varchar(99) not null, PREFS text not null, primary key (ID)); -create table SST_PRESENCES (ID bigint not null auto_increment, SITE_ID varchar(99) not null, USER_ID varchar(99) not null, P_DATE date not null, DURATION bigint default 0 not null, LAST_VISIT_START_TIME datetime default null, primary key (ID)); +create table SST_PRESENCES (ID bigint not null auto_increment, SITE_ID varchar(99) not null, USER_ID varchar(99) not null, P_DATE date not null, DURATION bigint default 0 not null, LAST_VISIT_START_TIME datetime default null, CURRENT_OPEN_SESSIONS int default 0 not null, primary key (ID)); create table SST_REPORTS (ID bigint not null auto_increment, SITE_ID varchar(99), TITLE varchar(255) not null, DESCRIPTION longtext, HIDDEN bit, REPORT_DEF text not null, CREATED_BY varchar(99) not null, CREATED_ON datetime not null, MODIFIED_BY varchar(99), MODIFIED_ON datetime, primary key (ID)); create table SST_RESOURCES (ID bigint not null auto_increment, USER_ID varchar(99) not null, SITE_ID varchar(99) not null, RESOURCE_REF varchar(255) not null, RESOURCE_ACTION varchar(12) not null, RESOURCE_DATE date not null, RESOURCE_COUNT bigint not null, primary key (ID)); create table SST_SITEACTIVITY (ID bigint not null auto_increment, SITE_ID varchar(99) not null, ACTIVITY_DATE date not null, EVENT_ID varchar(32) not null, ACTIVITY_COUNT bigint not null, primary key (ID)); diff --git a/sitestats/schema/db-tables/oracle/sitestats.sql b/sitestats/schema/db-tables/oracle/sitestats.sql index aaece2ef0d3e..4879b5fdf803 100644 --- a/sitestats/schema/db-tables/oracle/sitestats.sql +++ b/sitestats/schema/db-tables/oracle/sitestats.sql @@ -1,7 +1,7 @@ create table SST_EVENTS (ID number(19,0) not null, USER_ID varchar2(99 char) not null, SITE_ID varchar2(99 char) not null, EVENT_ID varchar2(32 char) not null, EVENT_DATE date not null, EVENT_COUNT number(19,0) not null, primary key (ID)); create table SST_JOB_RUN (ID number(19,0) not null, JOB_START_DATE timestamp, JOB_END_DATE timestamp, START_EVENT_ID number(19,0), END_EVENT_ID number(19,0), LAST_EVENT_DATE timestamp, primary key (ID)); create table SST_PREFERENCES (ID number(19,0) not null, SITE_ID varchar2(99 char) not null, PREFS clob not null, primary key (ID)); -create table SST_PRESENCES (ID number(19,0) not null, SITE_ID varchar2(99 char) not null, USER_ID varchar2(99 char) not null, P_DATE date not null, DURATION number(19,0) default 0 not null, LAST_VISIT_START_TIME timestamp default null, primary key (ID)); +create table SST_PRESENCES (ID number(19,0) not null, SITE_ID varchar2(99 char) not null, USER_ID varchar2(99 char) not null, P_DATE date not null, DURATION number(19,0) default 0 not null, LAST_VISIT_START_TIME timestamp default null, CURRENT_OPEN_SESSIONS number(10,0) default 0 not null, primary key (ID)); create table SST_REPORTS (ID number(19,0) not null, SITE_ID varchar2(99 char), TITLE varchar2(255 char) not null, DESCRIPTION clob, HIDDEN number(1,0), REPORT_DEF clob not null, CREATED_BY varchar2(99 char) not null, CREATED_ON timestamp not null, MODIFIED_BY varchar2(99 char), MODIFIED_ON timestamp, primary key (ID)); create table SST_RESOURCES (ID number(19,0) not null, USER_ID varchar2(99 char) not null, SITE_ID varchar2(99 char) not null, RESOURCE_REF varchar2(255 char) not null, RESOURCE_ACTION varchar2(12 char) not null, RESOURCE_DATE date not null, RESOURCE_COUNT number(19,0) not null, primary key (ID)); create table SST_SITEACTIVITY (ID number(19,0) not null, SITE_ID varchar2(99 char) not null, ACTIVITY_DATE date not null, EVENT_ID varchar2(32 char) not null, ACTIVITY_COUNT number(19,0) not null, primary key (ID)); diff --git a/sitestats/sitestats-api/src/java/org/sakaiproject/sitestats/api/SitePresence.java b/sitestats/sitestats-api/src/java/org/sakaiproject/sitestats/api/SitePresence.java index 0d02c6e97f66..16d2a269743c 100644 --- a/sitestats/sitestats-api/src/java/org/sakaiproject/sitestats/api/SitePresence.java +++ b/sitestats/sitestats-api/src/java/org/sakaiproject/sitestats/api/SitePresence.java @@ -41,4 +41,10 @@ public interface SitePresence extends Stat, Comparable { /** Set (temporary) last visit start time */ public void setLastVisitStartTime(Date lastVisitStartTime); + + /** Get previus open sessions */ + public int getCurrentOpenSessions(); + + /** Set current open sessions */ + public void setCurrentOpenSessions(int currentOpenSessions); } diff --git a/sitestats/sitestats-impl-hib/src/java/org/sakaiproject/sitestats/impl/SitePresenceImpl.java b/sitestats/sitestats-impl-hib/src/java/org/sakaiproject/sitestats/impl/SitePresenceImpl.java index ae80541c6a80..f15d481cec97 100644 --- a/sitestats/sitestats-impl-hib/src/java/org/sakaiproject/sitestats/impl/SitePresenceImpl.java +++ b/sitestats/sitestats-impl-hib/src/java/org/sakaiproject/sitestats/impl/SitePresenceImpl.java @@ -39,6 +39,7 @@ public class SitePresenceImpl implements SitePresence, Serializable { private Date date; private long duration; private Date lastVisitStartTime; + private int currentOpenSessions; @Override public int compareTo(SitePresence other) { @@ -67,7 +68,8 @@ public boolean equals(Object o) { && date.equals(other.getDate()) && duration == other.getDuration() && getCount() == other.getCount() - && lastVisitStartTime == other.getLastVisitStartTime(); + && lastVisitStartTime == other.getLastVisitStartTime() + && currentOpenSessions == other.getCurrentOpenSessions(); } @Override @@ -79,12 +81,13 @@ public int hashCode() { + this.getUserId().hashCode() + this.getDate().hashCode() + duration - + this.getLastVisitStartTime(); + + this.getLastVisitStartTime() + + currentOpenSessions; return hashStr.hashCode(); } public String toString(){ - return siteId + " : " + userId + " : " + date + " : " + duration + " (" + lastVisitStartTime + ")"; + return siteId + " : " + userId + " : " + date + " : " + duration + " (" + lastVisitStartTime + ") : " + currentOpenSessions; } public long getDuration() { @@ -143,4 +146,11 @@ public long getCount() { return duration; } + public int getCurrentOpenSessions() { + return currentOpenSessions; + } + + public void setCurrentOpenSessions(int currentOpenSessions) { + this.currentOpenSessions = currentOpenSessions; + } } diff --git a/sitestats/sitestats-impl-hib/src/java/org/sakaiproject/sitestats/impl/hbm/SitePresenceImpl.hbm.xml b/sitestats/sitestats-impl-hib/src/java/org/sakaiproject/sitestats/impl/hbm/SitePresenceImpl.hbm.xml index 4b2cd37a40ce..77bfd599ba1b 100644 --- a/sitestats/sitestats-impl-hib/src/java/org/sakaiproject/sitestats/impl/hbm/SitePresenceImpl.hbm.xml +++ b/sitestats/sitestats-impl-hib/src/java/org/sakaiproject/sitestats/impl/hbm/SitePresenceImpl.hbm.xml @@ -25,5 +25,8 @@ + + + diff --git a/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/PresenceConsolidation.java b/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/PresenceConsolidation.java index 5bdf51b9961a..6a2659a60edd 100644 --- a/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/PresenceConsolidation.java +++ b/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/PresenceConsolidation.java @@ -29,9 +29,11 @@ import java.util.function.Function; import org.sakaiproject.sitestats.api.presence.Presence; +import lombok.extern.slf4j.Slf4j; import lombok.NonNull; +@Slf4j public class PresenceConsolidation { @@ -54,6 +56,7 @@ public boolean add(@NonNull Presence presence) { if (records.contains(additialRecord)) { // Record is already present in list, leave unchanged + log.debug("Record already present: {}", additialRecord); return false; } @@ -73,10 +76,14 @@ public boolean add(@NonNull Presence presence) { } } + log.debug("First overlapping record: {}", firstOverlappingRecord); + log.debug("Last overlapping record: {}", lastOverlappingRecord); + // If we have no intersections, just add the new record if (firstOverlappingRecord == null && lastOverlappingRecord == null) { records.add(additialRecord); sort(); + log.debug("Added new record: {}", additialRecord); return true; } @@ -85,6 +92,7 @@ public boolean add(@NonNull Presence presence) { Presence onlyIntersectingRecord = firstOverlappingRecord; if (additialRecord.isWithin(onlyIntersectingRecord)) { + log.debug("New record is within the existing record: {}", additialRecord); return false; } @@ -99,6 +107,7 @@ public boolean add(@NonNull Presence presence) { onlyIntersectingRecord.setEnd(additionalRecordEnd); } + log.debug("Extended existing record: {}", onlyIntersectingRecord); return true; } @@ -113,6 +122,7 @@ public boolean add(@NonNull Presence presence) { PresenceRecord mergedRecord = merge(additialRecord, firstOverlappingRecord, lastOverlappingRecord); records.add(firstIndex, mergedRecord); + log.debug("Merged records into: {}", mergedRecord); return true; } @@ -155,6 +165,7 @@ public Map mapByDay() { // Split cross day records for (int i = 0; i < records.size(); i++) { PresenceRecord record = records.get(i); + log.debug("Processing record: {}", record); if (record.isCrossDay()) { Instant begin = record.getBegin(); @@ -162,6 +173,10 @@ public Map mapByDay() { Instant beginDay = toDay(begin); Instant endDay = toDay(end); + log.debug("Record crosses day: {}", record); + log.debug("Begin: {}, End: {}", begin, end); + log.debug("Begin day: {}, End day: {}", beginDay, endDay); + for (Instant day = beginDay; day.isBefore(end); day = day.plus(1, ChronoUnit.DAYS)) { @@ -170,6 +185,8 @@ public Map mapByDay() { Instant sliceBegin = day.equals(beginDay) ? begin : day; Instant sliceEnd = day.equals(endDay) ? end : day.plus(1, ChronoUnit.DAYS); + log.debug("Creating slice: Begin: {}, End: {}", sliceBegin, sliceEnd); + PresenceRecord presenceSlice = PresenceRecord.builder() .begin(sliceBegin) .end(sliceEnd) @@ -177,9 +194,11 @@ public Map mapByDay() { // Get or create presence consolidation for day and add record recordsByDay.computeIfAbsent(day, consolidationFacrory).add(presenceSlice); + log.debug("Added slice to day: {}, Slice: {}", day, presenceSlice); } } else { recordsByDay.computeIfAbsent(record.getDay(), consolidationFacrory).add(record); + log.debug("Added record to day: {}, Record: {}", record.getDay(), record); } } diff --git a/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/SitePresenceRecord.java b/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/SitePresenceRecord.java index 6e29d22566a1..eccc1c3182be 100644 --- a/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/SitePresenceRecord.java +++ b/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/SitePresenceRecord.java @@ -31,8 +31,13 @@ public class SitePresenceRecord extends PresenceRecord { private String siteId; private String userId; + private boolean originallyEnding; + public boolean isOriginallyEnding() { + return originallyEnding; + } + public SitePresenceKey getKey() { return SitePresenceKey.from(this); } diff --git a/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/StatsAggregateJobImpl.java b/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/StatsAggregateJobImpl.java index a960cf0f94b9..516ccb053c7f 100644 --- a/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/StatsAggregateJobImpl.java +++ b/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/StatsAggregateJobImpl.java @@ -153,7 +153,7 @@ public void execute(JobExecutionContext context) throws JobExecutionException { // check for SAKAI_EVENT.CONTEXT column try{ checkForContextColumn(); - log.debug("SAKAI_EVENT.CONTEXT exists? "+isEventContextSupported); + log.debug("SAKAI_EVENT.CONTEXT exists? {}", isEventContextSupported); }catch(SQLException e1){ log.warn("Unable to check existence of SAKAI_EVENT.CONTEXT", e1); } @@ -310,9 +310,12 @@ private String startJob() throws SQLException { if(firstEventIdProcessedInBlock == -1) firstEventIdProcessedInBlock = lastProcessedEventId; processedCounter++; + if (event == "pres.end" || event == "pres.begin") { + log.debug("Processed event: {}, date: {}, sessionUser: {}, sessionId: {}, eventId: {}", event, date, sessionUser, sessionId, lastProcessedEventId); + } }catch(Exception e){ if(log.isDebugEnabled()) - log.debug("Ignoring "+event+", "+ref+", "+date+", "+sessionUser+", "+sessionId+" due to: "+e.toString()); + log.debug("Ignoring {}, {}, {}, {}, {} due to: {}", event, ref, date, sessionUser, sessionId, e.toString()); } counter++; } @@ -320,6 +323,7 @@ private String startJob() throws SQLException { // If we didn't see a single event, time to break out and wrap up this job if (counter < 1) { + log.debug("No events found in this block, breaking out of the loop."); break; } @@ -335,6 +339,7 @@ private String startJob() throws SQLException { jobRun.setLastEventDate(lastEventDateWithSuccess); jobRun.setJobEndDate(new Date(System.currentTimeMillis())); saveJobRun(jobRun); + log.debug("Successfully processed events up to eventId: {}", lastProcessedEventIdWithSuccess); }else{ returnMessage = "An error occurred while processing/persisting events to db. Please check your logs, fix possible problems and re-run this job (will start after last successful processed event)."; log.error(returnMessage); @@ -456,7 +461,7 @@ public long collectPastSiteEvents(String siteId, Date initialDate, Date finalDat count++; }catch(Exception e){ if(log.isDebugEnabled()) - log.debug("Ignoring "+event+", "+ref+", "+date+", "+sessionUser+", "+sessionId+" due to: "+e.toString()); + log.debug("Ignoring {}, {}, {}, {}, {} due to: {}", event, ref, date, sessionUser, sessionId, e.toString()); } } diff --git a/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/StatsUpdateManagerImpl.java b/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/StatsUpdateManagerImpl.java index 409c68a9b345..e22a8d11dd99 100644 --- a/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/StatsUpdateManagerImpl.java +++ b/sitestats/sitestats-impl/src/java/org/sakaiproject/sitestats/impl/StatsUpdateManagerImpl.java @@ -533,7 +533,7 @@ private void preProcessEvent(Event event) { //it's a server event if(log.isDebugEnabled()) { - log.debug("Server event: "+e.toString()); + log.debug("Server event: {}", e.toString()); } String eventId = e.getEvent(); @@ -551,7 +551,7 @@ private void preProcessEvent(Event event) { //it's a user event if(log.isDebugEnabled()) { - log.debug("User event: "+e.toString()); + log.debug("User event: {}", e.toString()); } // user check @@ -955,7 +955,7 @@ private synchronized boolean doUpdateConsolidatedEvents() { return false; } long endTime = System.currentTimeMillis(); - log.debug("Time spent in doUpdateConsolidatedEvents(): " + (endTime-startTime) + " ms"); + log.debug("Time spent in doUpdateConsolidatedEvents(): {} ms", (endTime-startTime)); } return true; } @@ -1355,13 +1355,17 @@ private void doUpdateSitePresencesObjects(Session session, Collection allUserSitePresences = userSitePresencesEntry.getValue(); + log.debug("Processing siteId: {}, userId: {}, key: {}", siteId, userId, key); + for (int i = 0; i < allUserSitePresences.size(); i++) { log.debug("k{} p{}: {}", key, i, allUserSitePresences.get(i)); } Collections.sort(allUserSitePresences); + log.debug("Sorted presences for siteId: {}, userId: {}", siteId, userId); Optional savedBegin = doGetSavedBegin(session, siteId, userId); + log.debug("Saved begin for siteId: {}, userId: {}: {}", siteId, userId, savedBegin); List validUserSitePresences = new ArrayList<>(); @@ -1370,6 +1374,7 @@ private void doUpdateSitePresencesObjects(Session session, Collection savedOpenSessionsOpt = doGetOpenSessions(session, siteId, userId); + int savedOpenSessions = savedOpenSessionsOpt.orElse(0); + log.debug("savedOpenSessions: {}", savedOpenSessions); + + int currentOpenSessions = savedOpenSessions + (int) validUserSitePresences.stream() + .mapToInt(presence -> { + if (presence.isBeginning()) { + return 1; + } else if (presence.isOriginallyEnding()) { + return -1; + } else { + return 0; + } + }) + .sum(); + + log.debug("currentOpenSessions: {}", currentOpenSessions); + + if (!allUserSitePresences.isEmpty() && savedOpenSessions == 0) { + // All previus presences are ending + savedBegin = Optional.empty(); + } + + log.debug("Valid presences for siteId: {}, userId: {}: {}", siteId, userId, validUserSitePresences); + // Valid presences should have ony complete and beginning events // Get fist begin time of open unclosed session Optional firstBeginningPresenceBegin = validUserSitePresences.stream() @@ -1411,16 +1441,20 @@ private void doUpdateSitePresencesObjects(Session session, CollectionnaturalOrder().reversed()) .findFirst(); + log.debug("k{} lastEndingOrCompletePresenceEnd: {}, firstBeginningPresenceBegin: {}", key, lastEndingOrCompletePresenceEnd, firstBeginningPresenceBegin); + // The beginning presences will end at some point and will use this last start // For now we need to consider the beginning presence as complete until that point // This means we fill in lastStart for the end of beginning presences for (SitePresenceRecord presenceConsolidation : validUserSitePresences) { if (presenceConsolidation.isBeginning()) { - presenceConsolidation.setEnd(lastStart.get()); + presenceConsolidation.setEnd(lastStart.get()); + log.debug("k{} Set end for beginning presence: {}", key, presenceConsolidation); } } } else { lastStart = savedBegin.or(() -> firstBeginningPresenceBegin); + log.debug("k{} savedBegin: {}, firstBeginningPresenceBegin: {}, lastStart: {}", key, savedBegin, firstBeginningPresenceBegin, lastStart); } log.debug("k{} lastStart: {}", key, lastStart); @@ -1431,6 +1465,8 @@ private void doUpdateSitePresencesObjects(Session session, Collection { Long durationMillis = consodation.getDuration().toMillis(); // TODO: log the duration we are saving here - log.debug("k{} saving duration: {}", key, durationMillis); + log.debug("k{} saving duration for day {}: {}", key, day, durationMillis); + log.debug("consolidation: {}", consodation); SitePresence sitePresence = SitePresenceImpl.builder() .siteId(siteId) @@ -1446,6 +1483,7 @@ private void doUpdateSitePresencesObjects(Session session, Collection doGetSavedBegin(Session session, String siteId, String } } + private Optional doGetOpenSessions(Session session, String siteId, String userId) { + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(SitePresenceImpl.class); + Root root = cq.from(SitePresenceImpl.class); + cq.select(root); + cq.where(cb.and( + cb.equal(root.get("siteId"), siteId), + cb.equal(root.get("userId"), userId) + )); + + // Order by date in descending order to get the latest date + cq.orderBy(cb.desc(root.get("date"))); + + try { + return session.createQuery(cq).setMaxResults(1).uniqueResultOptional() + .map(SitePresenceImpl::getCurrentOpenSessions); + } catch (HibernateException e) { + log.error("Could not get previus open sessions for siteId [{}] and userId [{}] due to: {}", + siteId, userId, ExceptionUtils.getStackTrace(e)); + return Optional.empty(); + } + } + @SuppressWarnings("unchecked") private SitePresenceTotal doGetSitePresenceTotal(Session session, String siteId, String userId) { @@ -1669,9 +1732,9 @@ private String parseSiteId(Event e){ if(contextId != null && contextId.startsWith(sitePrefix)) { contextId = contextId.substring(sitePrefix.length()); } - log.debug("Context read from Event.getContext() for event: " + eventId + " - context: " + contextId); + log.debug("Context read from Event.getContext() for event: {} - context: {}", eventId, contextId); }catch(Exception ex){ - log.warn("Unable to get Event.getContext() for event: " + eventId, ex); + log.warn("Unable to get Event.getContext() for event: {}", eventId, ex); } if(contextId != null) return contextId; @@ -1725,18 +1788,18 @@ private Site getSite(String siteId) { String target = aliasService.getTarget(alias); if(target != null) { String newSiteId = entityManager.newReference(target).getId(); - log.debug(alias + " is an alias targetting site id: "+newSiteId); + log.debug("{} is an alias targetting site id: {}", alias, newSiteId); site = siteService.getSite(newSiteId); }else{ throw new IdUnusedException(siteId); } }catch(IdUnusedException e2){ // not a valid site - log.debug(siteId + " is not a valid site.", e2); + log.debug("{} is not a valid site.", siteId, e2); } }catch(Exception ex) { // not a valid site - log.debug(siteId + " is not a valid site.", ex); + log.debug("{} is not a valid site.", siteId, ex); } return site; } diff --git a/sitestats/sitestats-impl/src/test/org/sakaiproject/sitestats/test/PresenceConsolidationTest.java b/sitestats/sitestats-impl/src/test/org/sakaiproject/sitestats/test/PresenceConsolidationTest.java index 55d45e5a0158..a992f49bcbda 100644 --- a/sitestats/sitestats-impl/src/test/org/sakaiproject/sitestats/test/PresenceConsolidationTest.java +++ b/sitestats/sitestats-impl/src/test/org/sakaiproject/sitestats/test/PresenceConsolidationTest.java @@ -19,6 +19,10 @@ import java.time.Duration; import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; import java.time.temporal.ChronoUnit; import java.util.Map; @@ -27,8 +31,6 @@ import org.sakaiproject.sitestats.impl.PresenceConsolidation; import org.sakaiproject.sitestats.impl.PresenceRecord; -import lombok.NonNull; - public class PresenceConsolidationTest { @@ -38,7 +40,8 @@ public void testSerialPresences() { // p1: b----e // p2: b---------e // p3: b---------e - Instant base = Instant.now(); + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); Presence presence1 = PresenceRecord.builder() .begin(base) .end(base.plus(15, ChronoUnit.MINUTES)) @@ -70,7 +73,8 @@ public void testOverlappingPresences() { // p1: b-----------------------------e // p2: b----------------------------------e // p3: b---------e - Instant base = Instant.now(); + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); Presence presence1 = PresenceRecord.builder() .begin(base) .end(base.plus(90, ChronoUnit.MINUTES)) @@ -115,7 +119,8 @@ public void testCrossDayPresences() { // p1: b-------------------------e // p2: b-----------------------------------e // p3: b----------e - Instant base = PresenceConsolidation.toDay(Instant.now()); + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); Instant d1 = base; Instant d2 = base.plus(1, ChronoUnit.DAYS); Instant d3 = base.plus(2, ChronoUnit.DAYS); diff --git a/sitestats/sitestats-impl/src/test/org/sakaiproject/sitestats/test/StatsUpdateManagerTest.java b/sitestats/sitestats-impl/src/test/org/sakaiproject/sitestats/test/StatsUpdateManagerTest.java index 95b940759fce..014eeb363d0e 100644 --- a/sitestats/sitestats-impl/src/test/org/sakaiproject/sitestats/test/StatsUpdateManagerTest.java +++ b/sitestats/sitestats-impl/src/test/org/sakaiproject/sitestats/test/StatsUpdateManagerTest.java @@ -80,6 +80,11 @@ import lombok.extern.slf4j.Slf4j; +import java.time.LocalDateTime; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; + @ContextConfiguration(classes = {SiteStatsTestConfiguration.class}) @RunWith(SpringJUnit4ClassRunner.class) @Slf4j @@ -534,12 +539,196 @@ public void testSitePresencesTwoSitesSameUsers() throws InterruptedException { assertEquals(firstDuration + secondDuration, totalDuration); } + @Test + public void testSitePresence() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for one presences + // 0 20 40 60 + // eval + // p1: b----------e + // p2: + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + + // Evaluate at 60 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence1End))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(40, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + + @Test + public void testSitePresenceInterEvaluation() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for one presences + // 0 20 40 60 + // eval eval + // p1: b----------e + // p2: + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + + // Evaluate at 20 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(0, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 60 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(40, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + + @Test + public void testSuccessiveSitePresences() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for successive presences + // 0 20 40 60 80 100 120 140 + // eval + // p1: b----------e + // p2: b-------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(80, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(120, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + + // Evaluate at 140 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence1End, presence2Begin, presence2End))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + + @Test + public void testSuccessiveSitePresencesInterEvaluationCaseA() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for successive presences + // 0 20 40 60 80 100 120 140 + // eval eval eval + // p1: b----------e + // p2: b-------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(80, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(120, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + + // Evaluate at 60 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence1End))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(40, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + + // Evaluate at 100 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence2Begin))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(40, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 140 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence2End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + + @Test + public void testSuccessiveSitePresencesInterEvaluationCaseB() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for successive presences + // 0 20 40 60 80 100 120 140 + // eval eval eval + // p1: b----------e + // p2: b-------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(80, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(120, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + + // Evaluate at 20 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(0, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 60 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(40, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + + // Evaluate at 140 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence2Begin, presence2End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + @Test public void testOverlappingSitePresences() throws InterruptedException { - Instant base = Instant.now().truncatedTo(ChronoUnit.SECONDS); + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); // Create events for overlapping presences - // 0 20 40 60 + // 0 20 40 60 80 + // eval // p1: b-----------e // p2: b-----------e final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), @@ -551,25 +740,127 @@ public void testOverlappingSitePresences() throws InterruptedException { final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(60, ChronoUnit.SECONDS)), StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); - // Evaluate + // Evaluate at 80 seconds assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence2Begin, presence1End, presence2End))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(1, ChronoUnit.MINUTES).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + + @Test + public void testOverlappingSitePresencesInterEvaluationCaseA() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + // Create events for overlapping presences + // 0 20 40 60 80 100 120 140 + // eval eval eval eval + // p1: b-----------------------e + // p2: b-----------------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(80, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(120, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + + // Evaluate at 20 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin))); List results = db.getResultsForClass(SitePresenceImpl.class); assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(0, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 60 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence2Begin))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(0, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(2, result.getCurrentOpenSessions()); + + // Evaluate at 100 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 140 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence2End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(120, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + + @Test + public void testOverlappingSitePresencesInterEvaluationCaseB() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for overlapping presences + // 0 20 40 60 80 100 120 140 + // eval eval eval + // p1: b-----------------------e + // p2: b-----------------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(80, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(120, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + // Evaluate at 20 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(0, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 100 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence2Begin, presence1End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + // Evaluate at 140 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence2End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); assertEquals(FakeData.USER_A_ID, result.getUserId()); - assertEquals(Duration.of(1, ChronoUnit.MINUTES).toMillis(), result.getDuration()); + assertEquals(Duration.of(120, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); } @Test - public void testOverlappingSitePresencesInterEvaluation() throws InterruptedException { - Instant base = Instant.now().truncatedTo(ChronoUnit.SECONDS); + public void testOverlappingSitePresencesInterEvaluationCaseC() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); // Create events for overlapping presences // 0 20 40 60 80 100 120 140 - // eval eval eval eval + // eval eval eval // p1: b-----------------------e // p2: b-----------------------e final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), @@ -581,27 +872,425 @@ public void testOverlappingSitePresencesInterEvaluation() throws InterruptedExce final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(120, ChronoUnit.SECONDS)), StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); - SitePresenceImpl result; + // Evaluate at 20 seconds assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin))); - result = db.getResultsForClass(SitePresenceImpl.class).get(0); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); assertEquals(Duration.of(0, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + // Evaluate at 60 seconds assertTrue(statsUpdateManager.collectEvents(List.of(presence2Begin))); - result = db.getResultsForClass(SitePresenceImpl.class).get(0); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); assertEquals(Duration.of(0, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(2, result.getCurrentOpenSessions()); + // Evaluate at 140 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1End, presence2End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(120, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + + @Test + public void testCombinedOverlappingAndSuccessiveSitePresencesCaseA() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for overlapping and successive presences + // 0 20 40 60 80 100 120 140 160 180 200 220 240 260 280 300 + // eval + // p1: b-----------------------e + // p2: b------------------------e + // p3: b------------e + // p4: b------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(80, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(120, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence3Begin = statsUpdateManager.buildEvent(Date.from(base.plus(160, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_C_ID); + final Event presence3End = statsUpdateManager.buildEvent(Date.from(base.plus(200, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_C_ID); + final Event presence4Begin = statsUpdateManager.buildEvent(Date.from(base.plus(240, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_D_ID); + final Event presence4End = statsUpdateManager.buildEvent(Date.from(base.plus(280, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_D_ID); + + // Evaluate at 300 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence2Begin, presence1End, presence2End, presence3Begin, presence3End, presence4Begin, presence4End))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(200, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + + @Test + public void testCombinedOverlappingAndSuccessiveSitePresencesCaseAInterEvaluation() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for overlapping and successive presences + // 0 20 40 60 80 100 120 140 160 180 200 220 240 260 280 300 + // eval eval eval eval eval eval eval eval + // p1: b-----------------------e + // p2: b------------------------e + // p3: b------------e + // p4: b------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(80, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(120, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence3Begin = statsUpdateManager.buildEvent(Date.from(base.plus(160, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_C_ID); + final Event presence3End = statsUpdateManager.buildEvent(Date.from(base.plus(200, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_C_ID); + final Event presence4Begin = statsUpdateManager.buildEvent(Date.from(base.plus(240, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_D_ID); + final Event presence4End = statsUpdateManager.buildEvent(Date.from(base.plus(280, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_D_ID); + + // Evaluate at 20 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(0, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 60 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence2Begin))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(0, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(2, result.getCurrentOpenSessions()); + + // Evaluate at 100 seconds assertTrue(statsUpdateManager.collectEvents(List.of(presence1End))); - result = db.getResultsForClass(SitePresenceImpl.class).get(0); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + // Evaluate at 140 seconds assertTrue(statsUpdateManager.collectEvents(List.of(presence2End))); - result = db.getResultsForClass(SitePresenceImpl.class).get(0); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); assertEquals(Duration.of(120, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + + // Evaluate at 180 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence3Begin))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(120, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 220 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence3End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(160, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + + // Evaluate at 160 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence4Begin))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(160, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 300 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence4End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(200, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + + @Test + public void testCombinedOverlappingAndSuccessiveSitePresencesCaseB() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for overlapping and successive presences + // 0 20 40 60 80 100 120 140 160 180 200 220 240 260 280 300 + // eval + // p1: b-----------e + // p2: b------------e + // p2: b-------------------------e + // p3: b-------------------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(80, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(120, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence3Begin = statsUpdateManager.buildEvent(Date.from(base.plus(160, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_C_ID); + final Event presence3End = statsUpdateManager.buildEvent(Date.from(base.plus(240, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_C_ID); + final Event presence4Begin = statsUpdateManager.buildEvent(Date.from(base.plus(200, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_D_ID); + final Event presence4End = statsUpdateManager.buildEvent(Date.from(base.plus(280, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_D_ID); + + // Evaluate at 300 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence2Begin, presence1End, presence2End, presence3Begin, presence3End, presence4Begin, presence4End))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(200, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + + @Test + public void testCombinedOverlappingAndSuccessiveSitePresencesCaseBInterEvaluation() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for overlapping and successive presences + // 0 20 40 60 80 100 120 140 160 180 200 220 240 260 280 300 + // eval eval eval eval eval eval eval eval + // p1: b-----------e + // p2: b------------e + // p2: b-------------------------e + // p3: b-------------------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(80, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(120, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence3Begin = statsUpdateManager.buildEvent(Date.from(base.plus(160, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_C_ID); + final Event presence3End = statsUpdateManager.buildEvent(Date.from(base.plus(240, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_C_ID); + final Event presence4Begin = statsUpdateManager.buildEvent(Date.from(base.plus(200, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_D_ID); + final Event presence4End = statsUpdateManager.buildEvent(Date.from(base.plus(280, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_D_ID); + + // Evaluate at 20 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(0, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 60 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(40, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + + // Evaluate at 100 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence2Begin))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(40, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 140 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence2End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + + // Evaluate at 180 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence3Begin))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 220 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence4Begin))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(2, result.getCurrentOpenSessions()); + + // Evaluate at 260 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence3End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(160, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 300 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence4End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(200, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); } @Test public void testTrickyOverlappingSitePresences() throws InterruptedException { - Instant base = Instant.now().truncatedTo(ChronoUnit.SECONDS); + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for overlapping presences + // 0 20 40 60 80 100 + // eval + // p1: b-----------------------e + // p2: b-----e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(80, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(20, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + + // Evaluate at 100 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence1End, presence2Begin, presence2End))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + + @Test + public void testTrickyOverlappingSitePresencesInterEvaluationCaseA() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for overlapping presences + // 0 20 40 60 80 100 + // eval eval + // p1: b-----------------------e + // p2: b------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(80, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(60, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + + // Evaluate at 20 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(0, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 100 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence2Begin, presence2End, presence1End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + + @Test + public void testTrickyOverlappingSitePresencesInterEvaluationCaseB() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for overlapping presences + // 0 20 40 60 80 100 + // eval eval + // p1: b-----------------------e + // p2: b----------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(80, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(20, ChronoUnit.SECONDS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(60, ChronoUnit.SECONDS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + + // Evaluate at 40 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence2Begin))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(0, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(2, result.getCurrentOpenSessions()); + + // Evaluate at 100 seconds + assertTrue(statsUpdateManager.collectEvents(List.of(presence2End, presence1End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } + + @Test + public void testTrickyOverlappingSitePresencesInterEvaluationCaseC() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); // Create events for overlapping presences // 0 20 40 60 80 100 @@ -617,12 +1306,277 @@ public void testTrickyOverlappingSitePresences() throws InterruptedException { final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(40, ChronoUnit.SECONDS)), StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + // Evaluate at 60 seconds assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence2Begin, presence2End))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(40, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(1, result.getCurrentOpenSessions()); + + // Evaluate at 100 seconds assertTrue(statsUpdateManager.collectEvents(List.of(presence1End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + result = results.get(0); + assertEquals(FakeData.USER_A_ID, result.getUserId()); + assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + assertEquals(0, result.getCurrentOpenSessions()); + } - SitePresenceImpl result = db.getResultsForClass(SitePresenceImpl.class).get(0); + @Test + public void testSitePresenceTwoDays() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); - assertEquals(Duration.of(80, ChronoUnit.SECONDS).toMillis(), result.getDuration()); + // Create events for one presences + // 0 24 48 72 + // eval + // p1: b----------e + // p2: + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(48, ChronoUnit.HOURS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + + // Evaluate at 72 hours + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence1End))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(2, results.size()); + SitePresenceImpl resultFirstDay = results.get(0); + assertEquals(FakeData.USER_A_ID, resultFirstDay.getUserId()); + assertEquals(Duration.of(24, ChronoUnit.HOURS).toMillis(), resultFirstDay.getDuration()); + assertEquals(0, resultFirstDay.getCurrentOpenSessions()); + SitePresenceImpl resultSecondDay = results.get(1); + assertEquals(FakeData.USER_A_ID, resultSecondDay.getUserId()); + assertEquals(Duration.of(24, ChronoUnit.HOURS).toMillis(), resultSecondDay.getDuration()); + assertEquals(0, resultSecondDay.getCurrentOpenSessions()); + } + + @Test + public void testSuccessiveSitePresencesTwoDays() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for one presences + // 0 6 12 18 24 30 36 42 + // eval + // p1: b-----------e + // p2: b------------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(12, ChronoUnit.HOURS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(18, ChronoUnit.HOURS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(36, ChronoUnit.HOURS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + + // Evaluate at 42 hours + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence1End, presence2Begin, presence2End))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(2, results.size()); + SitePresenceImpl resultFirstDay = results.get(0); + assertEquals(FakeData.USER_A_ID, resultFirstDay.getUserId()); + assertEquals(Duration.of(18, ChronoUnit.HOURS).toMillis(), resultFirstDay.getDuration()); + assertEquals(0, resultFirstDay.getCurrentOpenSessions()); + SitePresenceImpl resultSecondDay = results.get(1); + assertEquals(FakeData.USER_A_ID, resultSecondDay.getUserId()); + assertEquals(Duration.of(12, ChronoUnit.HOURS).toMillis(), resultSecondDay.getDuration()); + assertEquals(0, resultSecondDay.getCurrentOpenSessions()); + } + + @Test + public void testSuccessiveSitePresencesTwoDaysInterEvaluation() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for one presences + // 0 6 12 18 24 30 36 42 + // eval eval + // p1: b-----e + // p2: b------------------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(6, ChronoUnit.HOURS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(12, ChronoUnit.HOURS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(36, ChronoUnit.HOURS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + + // Evaluate at 18 hours + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence1End, presence2Begin))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl resultFirstDay = results.get(0); + assertEquals(FakeData.USER_A_ID, resultFirstDay.getUserId()); + assertEquals(Duration.of(6, ChronoUnit.HOURS).toMillis(), resultFirstDay.getDuration()); + assertEquals(1, resultFirstDay.getCurrentOpenSessions()); + + // Evaluate at 42 hours + assertTrue(statsUpdateManager.collectEvents(List.of(presence2End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(2, results.size()); + resultFirstDay = results.get(0); + assertEquals(FakeData.USER_A_ID, resultFirstDay.getUserId()); + assertEquals(Duration.of(18, ChronoUnit.HOURS).toMillis(), resultFirstDay.getDuration()); + assertEquals(0, resultFirstDay.getCurrentOpenSessions()); + SitePresenceImpl resultSecondDay = results.get(1); + assertEquals(FakeData.USER_A_ID, resultSecondDay.getUserId()); + assertEquals(Duration.of(12, ChronoUnit.HOURS).toMillis(), resultSecondDay.getDuration()); + assertEquals(0, resultSecondDay.getCurrentOpenSessions()); + } + + @Test + public void testOverlappingSitePresenceTwoDays() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for one presences + // 0 6 12 18 24 30 36 42 + // eval + // p1: b-----------------------------e + // p2: b------------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(30, ChronoUnit.HOURS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(18, ChronoUnit.HOURS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(36, ChronoUnit.HOURS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + + // Evaluate at 42 hours + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence1End, presence2Begin, presence2End))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(2, results.size()); + SitePresenceImpl resultFirstDay = results.get(0); + assertEquals(FakeData.USER_A_ID, resultFirstDay.getUserId()); + assertEquals(Duration.of(24, ChronoUnit.HOURS).toMillis(), resultFirstDay.getDuration()); + assertEquals(0, resultFirstDay.getCurrentOpenSessions()); + SitePresenceImpl resultSecondDay = results.get(1); + assertEquals(FakeData.USER_A_ID, resultSecondDay.getUserId()); + assertEquals(Duration.of(12, ChronoUnit.HOURS).toMillis(), resultSecondDay.getDuration()); + assertEquals(0, resultSecondDay.getCurrentOpenSessions()); + } + + @Test + public void testOverlappingSitePresenceTwoDaysInterEvaluation() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for one presences + // 0 6 12 18 24 30 36 42 48 + // eval eval + // p1: b-----------------------------------e + // p2: b------------------------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(36, ChronoUnit.HOURS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(12, ChronoUnit.HOURS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(42, ChronoUnit.HOURS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + + // Evaluate at 18 hours + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence2Begin))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl resultFirstDay = results.get(0); + assertEquals(FakeData.USER_A_ID, resultFirstDay.getUserId()); + assertEquals(Duration.of(0, ChronoUnit.HOURS).toMillis(), resultFirstDay.getDuration()); + assertEquals(2, resultFirstDay.getCurrentOpenSessions()); + + // Evaluate at 48 hours + assertTrue(statsUpdateManager.collectEvents(List.of(presence1End, presence2End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(2, results.size()); + resultFirstDay = results.get(0); + assertEquals(FakeData.USER_A_ID, resultFirstDay.getUserId()); + assertEquals(Duration.of(24, ChronoUnit.HOURS).toMillis(), resultFirstDay.getDuration()); + assertEquals(0, resultFirstDay.getCurrentOpenSessions()); + SitePresenceImpl resultSecondDay = results.get(1); + assertEquals(FakeData.USER_A_ID, resultSecondDay.getUserId()); + assertEquals(Duration.of(18, ChronoUnit.HOURS).toMillis(), resultSecondDay.getDuration()); + assertEquals(0, resultSecondDay.getCurrentOpenSessions()); + } + + @Test + public void testTrickyOverlappingSitePresenceTwoDays() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for one presences + // 0 6 12 18 24 30 36 42 + // eval + // p1: b-----------------------------------e + // p2: b------------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(36, ChronoUnit.HOURS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(12, ChronoUnit.HOURS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(30, ChronoUnit.HOURS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + + // Evaluate at 42 hours + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence1End, presence2Begin, presence2End))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(2, results.size()); + SitePresenceImpl resultFirstDay = results.get(0); + assertEquals(FakeData.USER_A_ID, resultFirstDay.getUserId()); + assertEquals(Duration.of(24, ChronoUnit.HOURS).toMillis(), resultFirstDay.getDuration()); + assertEquals(0, resultFirstDay.getCurrentOpenSessions()); + SitePresenceImpl resultSecondDay = results.get(1); + assertEquals(FakeData.USER_A_ID, resultSecondDay.getUserId()); + assertEquals(Duration.of(12, ChronoUnit.HOURS).toMillis(), resultSecondDay.getDuration()); + assertEquals(0, resultSecondDay.getCurrentOpenSessions()); + } + + @Test + public void testTrickyOverlappingSitePresenceTwoDaysInterEvaluation() throws InterruptedException { + LocalDateTime midnight = LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT); + Instant base = midnight.atZone(ZoneId.systemDefault()).toInstant(); + + // Create events for one presences + // 0 6 12 18 24 30 36 42 + // eval eval + // p1: b-----------------------------------e + // p2: b------------e + final Event presence1Begin = statsUpdateManager.buildEvent(Date.from(base), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence1End = statsUpdateManager.buildEvent(Date.from(base.plus(36, ChronoUnit.HOURS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_A_ID); + final Event presence2Begin = statsUpdateManager.buildEvent(Date.from(base.plus(12, ChronoUnit.HOURS)), + StatsManager.SITEVISIT_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + final Event presence2End = statsUpdateManager.buildEvent(Date.from(base.plus(24, ChronoUnit.HOURS)), + StatsManager.SITEVISITEND_EVENTID, "/presence/" + FakeData.SITE_A_ID + PresenceService.PRESENCE_SUFFIX, null, FakeData.USER_A_ID, FakeData.SESSION_B_ID); + + // Evaluate at 18 hours + assertTrue(statsUpdateManager.collectEvents(List.of(presence1Begin, presence2Begin))); + List results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(1, results.size()); + SitePresenceImpl resultFirstDay = results.get(0); + assertEquals(FakeData.USER_A_ID, resultFirstDay.getUserId()); + assertEquals(Duration.of(0, ChronoUnit.HOURS).toMillis(), resultFirstDay.getDuration()); + assertEquals(2, resultFirstDay.getCurrentOpenSessions()); + + // Evaluate at 42 hours + assertTrue(statsUpdateManager.collectEvents(List.of(presence1End, presence2End))); + results = db.getResultsForClass(SitePresenceImpl.class); + assertEquals(2, results.size()); + resultFirstDay = results.get(0); + assertEquals(FakeData.USER_A_ID, resultFirstDay.getUserId()); + assertEquals(Duration.of(24, ChronoUnit.HOURS).toMillis(), resultFirstDay.getDuration()); + assertEquals(0, resultFirstDay.getCurrentOpenSessions()); + SitePresenceImpl resultSecondDay = results.get(1); + assertEquals(FakeData.USER_A_ID, resultSecondDay.getUserId()); + assertEquals(Duration.of(12, ChronoUnit.HOURS).toMillis(), resultSecondDay.getDuration()); + assertEquals(0, resultSecondDay.getCurrentOpenSessions()); } @SuppressWarnings("unchecked") diff --git a/sitestats/sitestats-impl/src/test/org/sakaiproject/sitestats/test/data/FakeData.java b/sitestats/sitestats-impl/src/test/org/sakaiproject/sitestats/test/data/FakeData.java index a1e8d43b7646..4d7a10872986 100644 --- a/sitestats/sitestats-impl/src/test/org/sakaiproject/sitestats/test/data/FakeData.java +++ b/sitestats/sitestats-impl/src/test/org/sakaiproject/sitestats/test/data/FakeData.java @@ -52,6 +52,8 @@ public class FakeData { public final static String SESSION_ID_PREFIX = "session-id-"; public final static String SESSION_A_ID = SESSION_ID_PREFIX + "a"; public final static String SESSION_B_ID = SESSION_ID_PREFIX + "b"; + public final static String SESSION_C_ID = SESSION_ID_PREFIX + "c"; + public final static String SESSION_D_ID = SESSION_ID_PREFIX + "d"; // TOOLs & EVENTs public final static String TOOL_CHAT = "sakai.chat";