Skip to content

Commit

Permalink
enable display of weekly stats
Browse files Browse the repository at this point in the history
fixes #2946
  • Loading branch information
matkoniecz committed Oct 29, 2022
1 parent c0ff036 commit 7923029
Show file tree
Hide file tree
Showing 22 changed files with 530 additions and 160 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class CountryStatisticsDaoTest : ApplicationDbTestCase() {
private lateinit var dao: CountryStatisticsDao

@Before fun createDao() {
dao = CountryStatisticsDao(database)
dao = CountryStatisticsDao(database, CountryStatisticsTables.NAME)
}

@Test fun addAndSubtract() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class EditTypeStatisticsDaoTest : ApplicationDbTestCase() {
private lateinit var daoType: EditTypeStatisticsDao

@Before fun createDao() {
daoType = EditTypeStatisticsDao(database)
daoType = EditTypeStatisticsDao(database, EditTypeStatisticsTables.NAME)
}

@Test fun getZero() {
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/de/westnordost/streetcomplete/Prefs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ object Prefs {
const val OSM_LOGGED_IN_AFTER_OAUTH_FUCKUP = "osm.logged_in_after_oauth_fuckup"
const val USER_DAYS_ACTIVE = "days_active"
const val USER_GLOBAL_RANK = "user_global_rank"
const val USER_GLOBAL_RANK_CURRENT_WEEK = "user_global_rank_current_week"
const val USER_LAST_TIMESTAMP_ACTIVE = "last_timestamp_active"
const val IS_SYNCHRONIZING_STATISTICS = "is_synchronizing_statistics"
const val TEAM_MODE_INDEX_IN_TEAM = "team_mode.index_in_team"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable
import de.westnordost.streetcomplete.data.osmnotes.notequests.NoteQuestsHiddenTable
import de.westnordost.streetcomplete.data.user.achievements.UserAchievementsTable
import de.westnordost.streetcomplete.data.user.achievements.UserLinksTable
import de.westnordost.streetcomplete.data.user.statistics.CountryStatisticsTable
import de.westnordost.streetcomplete.data.user.statistics.EditTypeStatisticsTable
import de.westnordost.streetcomplete.data.user.statistics.CountryStatisticsTables
import de.westnordost.streetcomplete.data.user.statistics.EditTypeStatisticsTables
import de.westnordost.streetcomplete.data.visiblequests.QuestPresetsTable
import de.westnordost.streetcomplete.data.visiblequests.QuestTypeOrderTable
import de.westnordost.streetcomplete.data.visiblequests.VisibleQuestTypeTable
Expand Down Expand Up @@ -84,8 +84,10 @@ class StreetCompleteSQLiteOpenHelper(context: Context, dbName: String) :
db.execSQL(DownloadedTilesTable.CREATE)

// user statistics
db.execSQL(EditTypeStatisticsTable.CREATE)
db.execSQL(CountryStatisticsTable.CREATE)
db.execSQL(EditTypeStatisticsTables.create(EditTypeStatisticsTables.NAME))
db.execSQL(EditTypeStatisticsTables.create(EditTypeStatisticsTables.NAME_CURRENT_WEEK))
db.execSQL(CountryStatisticsTables.create(CountryStatisticsTables.NAME))
db.execSQL(CountryStatisticsTables.create(CountryStatisticsTables.NAME_CURRENT_WEEK))
db.execSQL(UserAchievementsTable.CREATE)
db.execSQL(UserLinksTable.CREATE)

Expand Down Expand Up @@ -174,7 +176,11 @@ class StreetCompleteSQLiteOpenHelper(context: Context, dbName: String) :
if (oldVersion <= 5 && newVersion > 5) {
db.execSQL("ALTER TABLE ${NoteEditsTable.NAME} ADD COLUMN ${NoteEditsTable.Columns.TRACK} text DEFAULT '[]' NOT NULL")
}
if (oldVersion <= 6 && newVersion > 6) {
db.execSQL(EditTypeStatisticsTables.create(EditTypeStatisticsTables.NAME_CURRENT_WEEK))
db.execSQL(CountryStatisticsTables.create(CountryStatisticsTables.NAME_CURRENT_WEEK))
}
}
}

private const val DB_VERSION = 6
private const val DB_VERSION = 7
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,28 @@ package de.westnordost.streetcomplete.data.user.statistics

import de.westnordost.streetcomplete.data.CursorPosition
import de.westnordost.streetcomplete.data.Database
import de.westnordost.streetcomplete.data.user.statistics.CountryStatisticsTable.Columns.COUNTRY_CODE
import de.westnordost.streetcomplete.data.user.statistics.CountryStatisticsTable.Columns.RANK
import de.westnordost.streetcomplete.data.user.statistics.CountryStatisticsTable.Columns.SUCCEEDED
import de.westnordost.streetcomplete.data.user.statistics.CountryStatisticsTable.NAME
import de.westnordost.streetcomplete.data.user.statistics.CountryStatisticsTables.Columns.COUNTRY_CODE
import de.westnordost.streetcomplete.data.user.statistics.CountryStatisticsTables.Columns.RANK
import de.westnordost.streetcomplete.data.user.statistics.CountryStatisticsTables.Columns.SUCCEEDED

/** Stores how many quests the user solved in which country */
class CountryStatisticsDao(private val db: Database) {
class CountryStatisticsDao(private val db: Database, private val name: String) {

fun getCountryWithBiggestSolvedCount(): CountryStatistics? =
db.queryOne(NAME, orderBy = "$SUCCEEDED DESC") { it.toCountryStatistics() }
db.queryOne(name, orderBy = "$SUCCEEDED DESC") { it.toCountryStatistics() }

fun getAll(): List<CountryStatistics> =
db.query(NAME) { it.toCountryStatistics() }
db.query(name) { it.toCountryStatistics() }

fun clear() {
db.delete(NAME)
db.delete(name)
}

fun replaceAll(countriesStatistics: Collection<CountryStatistics>) {
db.transaction {
db.delete(NAME)
db.delete(name)
if (countriesStatistics.isNotEmpty()) {
db.replaceMany(NAME,
db.replaceMany(name,
arrayOf(COUNTRY_CODE, SUCCEEDED, RANK),
countriesStatistics.map { arrayOf(it.countryCode, it.count, it.rank) }
)
Expand All @@ -35,18 +34,18 @@ class CountryStatisticsDao(private val db: Database) {
fun addOne(countryCode: String) {
db.transaction {
// first ensure the row exists
db.insertOrIgnore(NAME, listOf(
db.insertOrIgnore(name, listOf(
COUNTRY_CODE to countryCode,
SUCCEEDED to 0
))

// then increase by one
db.exec("UPDATE $NAME SET $SUCCEEDED = $SUCCEEDED + 1 WHERE $COUNTRY_CODE = ?", arrayOf(countryCode))
db.exec("UPDATE $name SET $SUCCEEDED = $SUCCEEDED + 1 WHERE $COUNTRY_CODE = ?", arrayOf(countryCode))
}
}

fun subtractOne(countryCode: String) {
db.exec("UPDATE $NAME SET $SUCCEEDED = $SUCCEEDED - 1 WHERE $COUNTRY_CODE = ?", arrayOf(countryCode))
db.exec("UPDATE $name SET $SUCCEEDED = $SUCCEEDED - 1 WHERE $COUNTRY_CODE = ?", arrayOf(countryCode))
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package de.westnordost.streetcomplete.data.user.statistics

object CountryStatisticsTable {
object CountryStatisticsTables {
const val NAME = "country_statistics"
const val NAME_CURRENT_WEEK = "country_statistics_current_week"

object Columns {
const val COUNTRY_CODE = "country_code"
const val SUCCEEDED = "succeeded"
const val RANK = "rank"
}

const val CREATE = """
CREATE TABLE $NAME (
fun create(name: String) = """
CREATE TABLE $name (
${Columns.COUNTRY_CODE} varchar(255) PRIMARY KEY,
${Columns.SUCCEEDED} int NOT NULL,
${Columns.RANK} int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,27 @@ package de.westnordost.streetcomplete.data.user.statistics

import de.westnordost.streetcomplete.data.CursorPosition
import de.westnordost.streetcomplete.data.Database
import de.westnordost.streetcomplete.data.user.statistics.EditTypeStatisticsTable.Columns.ELEMENT_EDIT_TYPE
import de.westnordost.streetcomplete.data.user.statistics.EditTypeStatisticsTable.Columns.SUCCEEDED
import de.westnordost.streetcomplete.data.user.statistics.EditTypeStatisticsTable.NAME
import de.westnordost.streetcomplete.data.user.statistics.EditTypeStatisticsTables.Columns.ELEMENT_EDIT_TYPE
import de.westnordost.streetcomplete.data.user.statistics.EditTypeStatisticsTables.Columns.SUCCEEDED

/** Stores how many edits of which element type the user did */
class EditTypeStatisticsDao(private val db: Database) {
class EditTypeStatisticsDao(private val db: Database, private val name: String) {

fun getTotalAmount(): Int =
db.queryOne(NAME, arrayOf("total($SUCCEEDED) as count")) { it.getInt("count") } ?: 0
db.queryOne(name, arrayOf("total($SUCCEEDED) as count")) { it.getInt("count") } ?: 0

fun getAll(): List<EditTypeStatistics> =
db.query(NAME) { it.toEditTypeStatistics() }
db.query(name) { it.toEditTypeStatistics() }

fun clear() {
db.delete(NAME)
db.delete(name)
}

fun replaceAll(amounts: Map<String, Int>) {
db.transaction {
db.delete(NAME)
db.delete(name)
if (amounts.isNotEmpty()) {
db.replaceMany(NAME,
db.replaceMany(name,
arrayOf(ELEMENT_EDIT_TYPE, SUCCEEDED),
amounts.map { arrayOf(it.key, it.value) }
)
Expand All @@ -34,30 +33,30 @@ class EditTypeStatisticsDao(private val db: Database) {
fun addOne(type: String) {
db.transaction {
// first ensure the row exists
db.insertOrIgnore(NAME, listOf(
db.insertOrIgnore(name, listOf(
ELEMENT_EDIT_TYPE to type,
SUCCEEDED to 0
))

// then increase by one
db.exec("UPDATE $NAME SET $SUCCEEDED = $SUCCEEDED + 1 WHERE $ELEMENT_EDIT_TYPE = ?", arrayOf(type))
db.exec("UPDATE $name SET $SUCCEEDED = $SUCCEEDED + 1 WHERE $ELEMENT_EDIT_TYPE = ?", arrayOf(type))
}
}

fun subtractOne(type: String) {
db.exec("UPDATE $NAME SET $SUCCEEDED = $SUCCEEDED - 1 WHERE $ELEMENT_EDIT_TYPE = ?", arrayOf(type))
db.exec("UPDATE $name SET $SUCCEEDED = $SUCCEEDED - 1 WHERE $ELEMENT_EDIT_TYPE = ?", arrayOf(type))
}

fun getAmount(type: String): Int =
db.queryOne(NAME,
db.queryOne(name,
columns = arrayOf(SUCCEEDED),
where = "$ELEMENT_EDIT_TYPE = ?",
args = arrayOf(type)
) { it.getInt(SUCCEEDED) } ?: 0

fun getAmount(type: List<String>): Int {
val questionMarks = Array(type.size) { "?" }.joinToString(",")
return db.queryOne(NAME,
return db.queryOne(name,
columns = arrayOf("total($SUCCEEDED) as count"),
where = "$ELEMENT_EDIT_TYPE in ($questionMarks)",
args = type.toTypedArray()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ data class Statistics(
val countries: List<CountryStatistics>,
val rank: Int,
val daysActive: Int,
val currentWeekRank: Int,
val currentWeekTypes: List<EditTypeStatistics>,
val currentWeekCountries: List<CountryStatistics>,
val lastUpdate: Long,
val isAnalyzing: Boolean
val isAnalyzing: Boolean,
)

data class CountryStatistics(val countryCode: String, val count: Int, val rank: Int?)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import java.util.concurrent.FutureTask
class StatisticsController(
private val editTypeStatisticsDao: EditTypeStatisticsDao,
private val countryStatisticsDao: CountryStatisticsDao,
private val currentWeekEditTypeStatisticsDao: EditTypeStatisticsDao,
private val currentWeekCountryStatisticsDao: CountryStatisticsDao,
private val countryBoundaries: FutureTask<CountryBoundaries>,
private val prefs: SharedPreferences,
userLoginStatusSource: UserLoginStatusSource
Expand All @@ -45,6 +47,12 @@ class StatisticsController(
prefs.edit(true) { putInt(Prefs.USER_DAYS_ACTIVE, value) }
}

override var currentWeekRank: Int
get() = prefs.getInt(Prefs.USER_GLOBAL_RANK_CURRENT_WEEK, -1)
private set(value) {
prefs.edit(true) { putInt(Prefs.USER_GLOBAL_RANK_CURRENT_WEEK, value) }
}

override var isSynchronizing: Boolean
// default true because if it is not set yet, the first thing that is done is to synchronize it
get() = prefs.getBoolean(Prefs.IS_SYNCHRONIZING_STATISTICS, true)
Expand Down Expand Up @@ -80,16 +88,36 @@ class StatisticsController(
override fun getCountryStatisticsOfCountryWithBiggestSolvedCount() =
countryStatisticsDao.getCountryWithBiggestSolvedCount()

override fun getCurrentWeekEditCount(): Int =
currentWeekEditTypeStatisticsDao.getTotalAmount()

override fun getCurrentWeekEditTypeStatistics(): List<EditTypeStatistics> =
currentWeekEditTypeStatisticsDao.getAll()

override fun getCurrentWeekCountryStatistics(): List<CountryStatistics> =
currentWeekCountryStatisticsDao.getAll()

override fun getCurrentWeekCountryStatisticsOfCountryWithBiggestSolvedCount(): CountryStatistics? =
currentWeekCountryStatisticsDao.getCountryWithBiggestSolvedCount()

fun addOne(type: String, position: LatLon) {
editTypeStatisticsDao.addOne(type)
getRealCountryCode(position)?.let { countryStatisticsDao.addOne(it) }
currentWeekEditTypeStatisticsDao.addOne(type)
getRealCountryCode(position)?.let {
countryStatisticsDao.addOne(it)
currentWeekCountryStatisticsDao.addOne(it)
}
listeners.forEach { it.onAddedOne(type) }
updateDaysActive()
}

fun subtractOne(type: String, position: LatLon) {
editTypeStatisticsDao.subtractOne(type)
getRealCountryCode(position)?.let { countryStatisticsDao.subtractOne(it) }
currentWeekEditTypeStatisticsDao.subtractOne(type)
getRealCountryCode(position)?.let {
countryStatisticsDao.subtractOne(it)
currentWeekCountryStatisticsDao.subtractOne(it)
}
listeners.forEach { it.onSubtractedOne(type) }
updateDaysActive()
}
Expand All @@ -109,6 +137,9 @@ class StatisticsController(

editTypeStatisticsDao.replaceAll(statistics.types.associate { it.type to it.count })
countryStatisticsDao.replaceAll(statistics.countries)
currentWeekEditTypeStatisticsDao.replaceAll(statistics.currentWeekTypes.associate { it.type to it.count })
currentWeekCountryStatisticsDao.replaceAll(statistics.currentWeekCountries)
currentWeekRank = statistics.currentWeekRank
rank = statistics.rank
daysActive = statistics.daysActive
lastUpdate = statistics.lastUpdate
Expand All @@ -119,10 +150,13 @@ class StatisticsController(
private fun clear() {
editTypeStatisticsDao.clear()
countryStatisticsDao.clear()
currentWeekEditTypeStatisticsDao.clear()
currentWeekCountryStatisticsDao.clear()
prefs.edit(true) {
remove(Prefs.USER_DAYS_ACTIVE)
remove(Prefs.IS_SYNCHRONIZING_STATISTICS)
remove(Prefs.USER_GLOBAL_RANK)
remove(Prefs.USER_GLOBAL_RANK_CURRENT_WEEK)
remove(Prefs.USER_LAST_TIMESTAMP_ACTIVE)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,24 @@ import org.koin.dsl.module

private const val STATISTICS_BACKEND_URL = "https://www.westnordost.de/streetcomplete/statistics/"
val statisticsModule = module {
factory { CountryStatisticsDao(get()) }
factory { EditTypeStatisticsDao(get()) }

factory(named("EditTypeStatistics")) { EditTypeStatisticsDao(get(), EditTypeStatisticsTables.NAME) }
factory(named("CountryStatistics")) { CountryStatisticsDao(get(), CountryStatisticsTables.NAME) }

factory(named("EditTypeStatisticsCurrentWeek")) { EditTypeStatisticsDao(get(), EditTypeStatisticsTables.NAME_CURRENT_WEEK) }
factory(named("CountryStatisticsCurrentWeek")) { CountryStatisticsDao(get(), CountryStatisticsTables.NAME_CURRENT_WEEK) }

factory { StatisticsDownloader(STATISTICS_BACKEND_URL, get()) }
factory { StatisticsParser(get(named("TypeAliases"))) }

single<StatisticsSource> { get<StatisticsController>() }
single { StatisticsController(get(), get(), get(named("CountryBoundariesFuture")), get(), get()) }
single { StatisticsController(
editTypeStatisticsDao = get(named("EditTypeStatistics")),
countryStatisticsDao = get(named("CountryStatistics")),
currentWeekEditTypeStatisticsDao = get(named("EditTypeStatisticsCurrentWeek")),
currentWeekCountryStatisticsDao = get(named("CountryStatisticsCurrentWeek")),
countryBoundaries = get(named("CountryBoundariesFuture")),
prefs = get(),
userLoginStatusSource = get()
) }
}
Loading

0 comments on commit 7923029

Please sign in to comment.