parent
b5769173b7
commit
341cca8735
@ -0,0 +1,273 @@ |
||||
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 net.pokeranalytics.android.util.extensions.findById |
||||
import net.pokeranalytics.android.util.extensions.writeAsync |
||||
import kotlin.math.max |
||||
|
||||
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. |
||||
*/ |
||||
object SessionManager { |
||||
|
||||
private var sessions: RealmResults<Session>? = null |
||||
// private var results: RealmResults<Result>? = null |
||||
|
||||
private var dateModifiedSessionIds: MutableSet<String> = mutableSetOf() |
||||
private var netModifiedSessionIds: MutableSet<String> = mutableSetOf() |
||||
private var statsToComputeSessionIds: MutableSet<String> = mutableSetOf() |
||||
|
||||
init { |
||||
|
||||
val realm = Realm.getDefaultInstance() |
||||
|
||||
this.sessions = realm.where(Session::class.java).findAll() |
||||
this.sessions?.addChangeListener { results -> |
||||
|
||||
if (this.dateModifiedSessionIds.isNotEmpty()) { |
||||
results.realm.writeAsync { asyncRealm -> |
||||
for (sessionId in dateModifiedSessionIds) { |
||||
asyncRealm.findById<Session>(sessionId)?.let { session -> |
||||
if (session.endDate != null) { |
||||
this.updateTimeline(session) |
||||
} else if (session.sessionSet != null) { |
||||
this.removeFromTimeline(session) |
||||
} |
||||
} |
||||
} |
||||
this.dateModifiedSessionIds.clear() |
||||
} |
||||
} |
||||
|
||||
if (this.netModifiedSessionIds.isNotEmpty()) { |
||||
results.realm.writeAsync { asyncRealm -> |
||||
for (sessionId in netModifiedSessionIds) { |
||||
asyncRealm.findById<Session>(sessionId)?.computeNet(false) |
||||
} |
||||
this.dateModifiedSessionIds.clear() |
||||
} |
||||
} |
||||
|
||||
if (this.statsToComputeSessionIds.isNotEmpty()) { |
||||
results.realm.writeAsync { asyncRealm -> |
||||
for (sessionId in statsToComputeSessionIds) { |
||||
asyncRealm.findById<Session>(sessionId)?.computeStats() |
||||
} |
||||
this.statsToComputeSessionIds.clear() |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
realm.close() |
||||
} |
||||
|
||||
fun create() { |
||||
// empty, juste called to make sure the singleton is initialized |
||||
} |
||||
|
||||
fun sessionDateChanged(session: Session) { |
||||
this.dateModifiedSessionIds.add(session.id) |
||||
} |
||||
fun sessionNetChanged(session: Session) { |
||||
this.netModifiedSessionIds.add(session.id) |
||||
} |
||||
fun sessionToCompute(session: Session) { |
||||
this.statsToComputeSessionIds.add(session.id) |
||||
} |
||||
|
||||
/** |
||||
* 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() |
||||
|
||||
// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}") |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Removes the [session] from the timeline |
||||
*/ |
||||
fun removeFromTimeline(session: Session) { |
||||
|
||||
if (!session.realm.isInTransaction) { |
||||
throw PAIllegalStateException("realm should be in transaction at this point") |
||||
} |
||||
|
||||
val sessionSet = session.sessionSet |
||||
if (sessionSet != null) { |
||||
|
||||
val sessions = mutableSetOf<Session>() |
||||
sessionSet.sessions?.asIterable()?.let { sessions.addAll(it) } |
||||
sessions.remove(session) |
||||
|
||||
sessionSet.deleteFromRealm() |
||||
|
||||
sessions.forEach { |
||||
updateTimeline(it) |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,209 +0,0 @@ |
||||
package net.pokeranalytics.android.model.utils |
||||
|
||||
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 |
||||
|
||||
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) |
||||
} |
||||
|
||||
// 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() |
||||
|
||||
// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}") |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Removes the [session] from the timeline |
||||
*/ |
||||
fun removeFromTimeline(session: Session) { |
||||
|
||||
if (!session.realm.isInTransaction) { |
||||
throw PAIllegalStateException("realm should be in transaction at this point") |
||||
} |
||||
|
||||
val sessionSet = session.sessionSet |
||||
if (sessionSet != null) { |
||||
|
||||
val sessions = mutableSetOf<Session>() |
||||
sessionSet.sessions?.asIterable()?.let { sessions.addAll(it) } |
||||
sessions.remove(session) |
||||
|
||||
sessionSet.deleteFromRealm() |
||||
|
||||
sessions.forEach { |
||||
updateTimeline(it) |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue