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