|
|
|
|
@ -1,9 +1,9 @@ |
|
|
|
|
package net.pokeranalytics.android.model.utils |
|
|
|
|
|
|
|
|
|
import io.realm.Realm |
|
|
|
|
import io.realm.RealmQuery |
|
|
|
|
import io.realm.RealmResults |
|
|
|
|
import net.pokeranalytics.android.exceptions.ModelException |
|
|
|
|
import net.pokeranalytics.android.exceptions.PAIllegalStateException |
|
|
|
|
import net.pokeranalytics.android.model.realm.Session |
|
|
|
|
import net.pokeranalytics.android.model.realm.SessionSet |
|
|
|
|
import kotlin.math.max |
|
|
|
|
@ -14,196 +14,228 @@ class CorruptSessionSetException(message: String) : Exception(message) |
|
|
|
|
* The manager is in charge of updating the abstract concept of timeline, |
|
|
|
|
* representing the sequenced time frames where the user plays. |
|
|
|
|
*/ |
|
|
|
|
class SessionSetManager { |
|
|
|
|
|
|
|
|
|
companion object { |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Updates the global timeline using the updated [session] |
|
|
|
|
*/ |
|
|
|
|
fun updateTimeline(session: Session) { |
|
|
|
|
|
|
|
|
|
if (!session.realm.isInTransaction) { |
|
|
|
|
throw PAIllegalStateException("realm should be in transaction at this point") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (session.startDate == null) { |
|
|
|
|
throw ModelException("Start date should never be null here") |
|
|
|
|
} |
|
|
|
|
if (session.endDate == null) { |
|
|
|
|
throw ModelException("End date should never be null here") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
val sessionSets = this.matchingSets(session) |
|
|
|
|
cleanupSessionSets(session, sessionSets) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun matchingSets(session: Session) : RealmResults<SessionSet> { |
|
|
|
|
val realm = session.realm |
|
|
|
|
val endDate = session.endDate!! // tested above |
|
|
|
|
val startDate = session.startDate!! |
|
|
|
|
|
|
|
|
|
val query: RealmQuery<SessionSet> = realm.where(SessionSet::class.java) |
|
|
|
|
|
|
|
|
|
query |
|
|
|
|
.lessThanOrEqualTo("startDate", startDate) |
|
|
|
|
.greaterThanOrEqualTo("endDate", startDate) |
|
|
|
|
.or() |
|
|
|
|
.lessThanOrEqualTo("startDate", endDate) |
|
|
|
|
.greaterThanOrEqualTo("endDate", endDate) |
|
|
|
|
.or() |
|
|
|
|
.greaterThanOrEqualTo("startDate", startDate) |
|
|
|
|
.lessThanOrEqualTo("endDate", endDate) |
|
|
|
|
|
|
|
|
|
return query.findAll() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Multiple session sets update: |
|
|
|
|
* Merges or splits session sets |
|
|
|
|
* Does that by deleting then recreating |
|
|
|
|
*/ |
|
|
|
|
private fun cleanupSessionSets(session: Session, sessionSets: RealmResults<SessionSet>) { |
|
|
|
|
|
|
|
|
|
// get all endedSessions from sets |
|
|
|
|
val allImpactedSessions = mutableSetOf<Session>() |
|
|
|
|
sessionSets.forEach { set -> |
|
|
|
|
set.sessions?.asIterable()?.let { allImpactedSessions.addAll(it) } |
|
|
|
|
} |
|
|
|
|
allImpactedSessions.add(session) |
|
|
|
|
|
|
|
|
|
// delete all sets |
|
|
|
|
sessionSets.deleteAllFromRealm() |
|
|
|
|
|
|
|
|
|
allImpactedSessions.forEach { impactedSession -> |
|
|
|
|
val sets = matchingSets(impactedSession) |
|
|
|
|
this.updateTimeFrames(sets, impactedSession) |
|
|
|
|
} |
|
|
|
|
object SessionSetManager { |
|
|
|
|
|
|
|
|
|
var sessions: RealmResults<Session> |
|
|
|
|
|
|
|
|
|
private val sessionsToProcess = mutableSetOf<Session>() |
|
|
|
|
|
|
|
|
|
init { |
|
|
|
|
|
|
|
|
|
val realm = Realm.getDefaultInstance() |
|
|
|
|
|
|
|
|
|
this.sessions = realm.where(Session::class.java).findAll() |
|
|
|
|
this.sessions.addChangeListener { sessions, changeSet -> |
|
|
|
|
changeSet.insertions.forEach { index -> |
|
|
|
|
sessions[index]?.let { this.sessionsToProcess.add(it) } |
|
|
|
|
} |
|
|
|
|
changeSet.changes.forEach { index -> |
|
|
|
|
sessions[index]?.let { this.sessionsToProcess.add(it) } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
processSessions() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
realm.close() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// fun updatedSession(session: Session) { |
|
|
|
|
// this.sessionsToProcess.add(session) |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
private fun processSessions() { |
|
|
|
|
for (session in this.sessionsToProcess) { |
|
|
|
|
if (session.startDate != null && session.endDate != null) { |
|
|
|
|
this.updateTimeline(session) |
|
|
|
|
} else if (session.sessionSet != null) { |
|
|
|
|
removeFromTimeline(session) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
this.sessionsToProcess.clear() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Updates the global timeline using the updated [session] |
|
|
|
|
*/ |
|
|
|
|
fun updateTimeline(session: Session) { |
|
|
|
|
|
|
|
|
|
// if (!session.realm.isInTransaction) { |
|
|
|
|
// throw PAIllegalStateException("realm should be in transaction at this point") |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
if (session.startDate == null) { |
|
|
|
|
throw ModelException("Start date should never be null here") |
|
|
|
|
} |
|
|
|
|
if (session.endDate == null) { |
|
|
|
|
throw ModelException("End date should never be null here") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
val sessionSets = this.matchingSets(session) |
|
|
|
|
cleanupSessionSets(session, sessionSets) |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun matchingSets(session: Session): RealmResults<SessionSet> { |
|
|
|
|
val realm = session.realm |
|
|
|
|
val endDate = session.endDate!! // tested above |
|
|
|
|
val startDate = session.startDate!! |
|
|
|
|
|
|
|
|
|
val query: RealmQuery<SessionSet> = realm.where(SessionSet::class.java) |
|
|
|
|
|
|
|
|
|
query |
|
|
|
|
.lessThanOrEqualTo("startDate", startDate) |
|
|
|
|
.greaterThanOrEqualTo("endDate", startDate) |
|
|
|
|
.or() |
|
|
|
|
.lessThanOrEqualTo("startDate", endDate) |
|
|
|
|
.greaterThanOrEqualTo("endDate", endDate) |
|
|
|
|
.or() |
|
|
|
|
.greaterThanOrEqualTo("startDate", startDate) |
|
|
|
|
.lessThanOrEqualTo("endDate", endDate) |
|
|
|
|
|
|
|
|
|
return query.findAll() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Multiple session sets update: |
|
|
|
|
* Merges or splits session sets |
|
|
|
|
* Does that by deleting then recreating |
|
|
|
|
*/ |
|
|
|
|
private fun cleanupSessionSets(session: Session, sessionSets: RealmResults<SessionSet>) { |
|
|
|
|
|
|
|
|
|
// get all endedSessions from sets |
|
|
|
|
val allImpactedSessions = mutableSetOf<Session>() |
|
|
|
|
sessionSets.forEach { set -> |
|
|
|
|
set.sessions?.asIterable()?.let { allImpactedSessions.addAll(it) } |
|
|
|
|
} |
|
|
|
|
allImpactedSessions.add(session) |
|
|
|
|
|
|
|
|
|
// delete all sets |
|
|
|
|
sessionSets.deleteAllFromRealm() |
|
|
|
|
|
|
|
|
|
allImpactedSessions.forEach { impactedSession -> |
|
|
|
|
val sets = matchingSets(impactedSession) |
|
|
|
|
this.updateTimeFrames(sets, impactedSession) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}") |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Update the global timeline using the impacted [sessionSets] and the updated [session] |
|
|
|
|
*/ |
|
|
|
|
private fun updateTimeFrames(sessionSets: RealmResults<SessionSet>, session: Session) { |
|
|
|
|
|
|
|
|
|
when (sessionSets.size) { |
|
|
|
|
0 -> this.createOrUpdateSessionSet(session) |
|
|
|
|
else -> this.mergeSessionGroups(session, sessionSets) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Creates or update the session set for the [session] |
|
|
|
|
*/ |
|
|
|
|
private fun createOrUpdateSessionSet(session: Session) { |
|
|
|
|
|
|
|
|
|
val set = session.sessionSet |
|
|
|
|
if (set != null) { |
|
|
|
|
set.startDate = session.startDate!! // tested above |
|
|
|
|
set.endDate = session.endDate!! |
|
|
|
|
} else { |
|
|
|
|
this.createSessionSet(session) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Create a set and affect it to the [session] |
|
|
|
|
*/ |
|
|
|
|
private fun createSessionSet(session: Session) { |
|
|
|
|
val set: SessionSet = SessionSet.newInstance(session.realm) |
|
|
|
|
set.startDate = session.startDate!! |
|
|
|
|
set.endDate = session.endDate!! |
|
|
|
|
set.breakDuration = session.breakDuration |
|
|
|
|
session.sessionSet = set |
|
|
|
|
set.computeStats() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Multiple session sets update: |
|
|
|
|
* Merges all sets into one (delete all then create a new one) |
|
|
|
|
*/ |
|
|
|
|
private fun mergeSessionGroups(session: Session, sessionSets: RealmResults<SessionSet>) { |
|
|
|
|
|
|
|
|
|
var startDate = session.startDate!! |
|
|
|
|
var endDate = session.endDate!! |
|
|
|
|
|
|
|
|
|
// get all endedSessions from sets |
|
|
|
|
val sessions = mutableSetOf<Session>() |
|
|
|
|
sessionSets.forEach { set -> |
|
|
|
|
set.sessions?.asIterable()?.let { sessions.addAll(it) } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// find earlier and later dates from all sets |
|
|
|
|
sessions.forEach { s -> |
|
|
|
|
|
|
|
|
|
if (s.startDate != null && s.endDate != null) { |
|
|
|
|
val start = s.startDate!! |
|
|
|
|
val end = s.endDate!! |
|
|
|
|
if (start.before(startDate)) { |
|
|
|
|
startDate = start |
|
|
|
|
} |
|
|
|
|
if (end.after(endDate)) { |
|
|
|
|
endDate = end |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
throw CorruptSessionSetException("Set contains unfinished sessions!") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// delete all sets |
|
|
|
|
sessionSets.deleteAllFromRealm() |
|
|
|
|
|
|
|
|
|
// Create a new set |
|
|
|
|
val set: SessionSet = SessionSet.newInstance(session.realm) |
|
|
|
|
set.startDate = startDate |
|
|
|
|
set.endDate = endDate |
|
|
|
|
|
|
|
|
|
// Add the session linked to this timeframe to the new sessionGroup |
|
|
|
|
session.sessionSet = set |
|
|
|
|
|
|
|
|
|
// Add all orphan endedSessions |
|
|
|
|
sessions.forEach { s -> |
|
|
|
|
s.sessionSet = set |
|
|
|
|
set.breakDuration = max(set.breakDuration, s.breakDuration) |
|
|
|
|
} |
|
|
|
|
set.computeStats() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Update the global timeline using the impacted [sessionSets] and the updated [session] |
|
|
|
|
*/ |
|
|
|
|
private fun updateTimeFrames(sessionSets: RealmResults<SessionSet>, session: Session) { |
|
|
|
|
|
|
|
|
|
when (sessionSets.size) { |
|
|
|
|
0 -> this.createOrUpdateSessionSet(session) |
|
|
|
|
else -> this.mergeSessionGroups(session, sessionSets) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Creates or update the session set for the [session] |
|
|
|
|
*/ |
|
|
|
|
private fun createOrUpdateSessionSet(session: Session) { |
|
|
|
|
|
|
|
|
|
val set = session.sessionSet |
|
|
|
|
if (set != null) { |
|
|
|
|
set.startDate = session.startDate!! // tested above |
|
|
|
|
set.endDate = session.endDate!! |
|
|
|
|
} else { |
|
|
|
|
this.createSessionSet(session) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Create a set and affect it to the [session] |
|
|
|
|
*/ |
|
|
|
|
private fun createSessionSet(session: Session) { |
|
|
|
|
val set = SessionSet() //.newInstance(session.realm) |
|
|
|
|
set.startDate = session.startDate!! |
|
|
|
|
set.endDate = session.endDate!! |
|
|
|
|
set.breakDuration = session.breakDuration |
|
|
|
|
session.sessionSet = set |
|
|
|
|
set.computeStats() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Multiple session sets update: |
|
|
|
|
* Merges all sets into one (delete all then create a new one) |
|
|
|
|
*/ |
|
|
|
|
private fun mergeSessionGroups(session: Session, sessionSets: RealmResults<SessionSet>) { |
|
|
|
|
|
|
|
|
|
var startDate = session.startDate!! |
|
|
|
|
var endDate = session.endDate!! |
|
|
|
|
|
|
|
|
|
// get all endedSessions from sets |
|
|
|
|
val sessions = mutableSetOf<Session>() |
|
|
|
|
sessionSets.forEach { set -> |
|
|
|
|
set.sessions?.asIterable()?.let { sessions.addAll(it) } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// find earlier and later dates from all sets |
|
|
|
|
sessions.forEach { s -> |
|
|
|
|
|
|
|
|
|
if (s.startDate != null && s.endDate != null) { |
|
|
|
|
val start = s.startDate!! |
|
|
|
|
val end = s.endDate!! |
|
|
|
|
if (start.before(startDate)) { |
|
|
|
|
startDate = start |
|
|
|
|
} |
|
|
|
|
if (end.after(endDate)) { |
|
|
|
|
endDate = end |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
throw CorruptSessionSetException("Set contains unfinished sessions!") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// delete all sets |
|
|
|
|
sessionSets.deleteAllFromRealm() |
|
|
|
|
|
|
|
|
|
// Create a new set |
|
|
|
|
val set = SessionSet() //.newInstance(session.realm) |
|
|
|
|
set.startDate = startDate |
|
|
|
|
set.endDate = endDate |
|
|
|
|
|
|
|
|
|
// Add the session linked to this timeframe to the new sessionGroup |
|
|
|
|
session.sessionSet = set |
|
|
|
|
|
|
|
|
|
// Add all orphan endedSessions |
|
|
|
|
sessions.forEach { s -> |
|
|
|
|
s.sessionSet = set |
|
|
|
|
set.breakDuration = max(set.breakDuration, s.breakDuration) |
|
|
|
|
} |
|
|
|
|
set.computeStats() |
|
|
|
|
|
|
|
|
|
// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}") |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Removes the [session] from the timeline |
|
|
|
|
*/ |
|
|
|
|
fun removeFromTimeline(session: Session) { |
|
|
|
|
/** |
|
|
|
|
* Removes the [session] from the timeline |
|
|
|
|
*/ |
|
|
|
|
fun removeFromTimeline(session: Session) { |
|
|
|
|
|
|
|
|
|
if (!session.realm.isInTransaction) { |
|
|
|
|
throw PAIllegalStateException("realm should be in transaction at this point") |
|
|
|
|
} |
|
|
|
|
// if (!session.realm.isInTransaction) { |
|
|
|
|
// throw PAIllegalStateException("realm should be in transaction at this point") |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
val sessionSet = session.sessionSet |
|
|
|
|
if (sessionSet != null) { |
|
|
|
|
val sessionSet = session.sessionSet |
|
|
|
|
if (sessionSet != null) { |
|
|
|
|
|
|
|
|
|
val sessions = mutableSetOf<Session>() |
|
|
|
|
sessionSet.sessions?.asIterable()?.let { sessions.addAll(it) } |
|
|
|
|
sessions.remove(session) |
|
|
|
|
val sessions = mutableSetOf<Session>() |
|
|
|
|
sessionSet.sessions?.asIterable()?.let { sessions.addAll(it) } |
|
|
|
|
sessions.remove(session) |
|
|
|
|
|
|
|
|
|
sessionSet.deleteFromRealm() |
|
|
|
|
sessionSet.deleteFromRealm() |
|
|
|
|
|
|
|
|
|
sessions.forEach { |
|
|
|
|
updateTimeline(it) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
sessions.forEach { |
|
|
|
|
updateTimeline(it) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |