Skip to content

Commit

Permalink
Merge branch 'main' into Notification_time_ago
Browse files Browse the repository at this point in the history
  • Loading branch information
Isira-Seneviratne authored Apr 1, 2023
2 parents f7ec959 + 6cc7458 commit 2b57d8a
Show file tree
Hide file tree
Showing 62 changed files with 1,319 additions and 139 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ android {
applicationId 'org.wikipedia'
minSdkVersion 21
targetSdkVersion 33
versionCode 50433
versionCode 50434
testApplicationId 'org.wikipedia.test'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments clearPackageData: 'true'
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/org/wikipedia/WikipediaApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class WikipediaApp : Application() {
val mainThreadHandler by lazy { Handler(mainLooper) }
val languageState by lazy { AppLanguageState(this) }
val appSessionEvent by lazy { AppSessionEvent() }

val userAgent by lazy {
var channel = ReleaseUtil.getChannel(this)
channel = if (channel.isBlank()) "" else " $channel"
Expand Down
31 changes: 31 additions & 0 deletions app/src/main/java/org/wikipedia/analytics/eventplatform/ABTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.wikipedia.analytics.eventplatform

import org.wikipedia.settings.PrefsIoUtil
import kotlin.random.Random

open class ABTest(private val abTestName: String, private val abTestGroupCount: Int) {

val group: Int
get() {
testGroup = PrefsIoUtil.getInt(AB_TEST_KEY_PREFIX + abTestName, -1)
if (testGroup == -1) {
assignGroup()
PrefsIoUtil.setInt(AB_TEST_KEY_PREFIX + abTestName, testGroup)
}
return testGroup
}

protected var testGroup: Int = -1

protected open fun assignGroup() {
testGroup = Random(System.currentTimeMillis()).nextInt(Int.MAX_VALUE).mod(abTestGroupCount)
}

companion object {
private const val AB_TEST_KEY_PREFIX = "ab_test_"
const val GROUP_SIZE_2 = 2
const val GROUP_1 = 0
const val GROUP_2 = 1
const val GROUP_3 = 2
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.wikipedia.analytics.eventplatform

import kotlinx.coroutines.runBlocking
import org.wikipedia.WikipediaApp
import org.wikipedia.auth.AccountUtil
import org.wikipedia.settings.Prefs
import org.wikipedia.util.log.L

class MachineGeneratedArticleDescriptionABCTest : ABTest("mBART25", GROUP_SIZE_2) {

override fun assignGroup() {
super.assignGroup()
if (AccountUtil.isLoggedIn) {
runBlocking {
try {
MachineGeneratedArticleDescriptionsAnalyticsHelper.setUserExperienced()
if (testGroup == GROUP_2 && Prefs.suggestedEditsMachineGeneratedDescriptionsIsExperienced) {
testGroup = GROUP_3
}
} catch (e: Exception) {
L.e(e)
}
}
}
MachineGeneratedArticleDescriptionsAnalyticsHelper().logGroupAssigned(WikipediaApp.instance, testGroup)
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,121 @@
package org.wikipedia.analytics.eventplatform

import android.content.Context
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.wikipedia.WikipediaApp
import org.wikipedia.auth.AccountUtil
import org.wikipedia.dataclient.ServiceFactory
import org.wikipedia.page.PageTitle
import org.wikipedia.settings.Prefs
import org.wikipedia.util.ActiveTimer

object MachineGeneratedArticleDescriptionsAnalyticsHelper {
class MachineGeneratedArticleDescriptionsAnalyticsHelper {

private var apiFailed = false
var apiOrderList = emptyList<String>()
var displayOrderList = emptyList<String>()
private var chosenSuggestion = ""
val timer = ActiveTimer()

fun articleDescriptionEditingStart(context: Context) {
EventPlatformClient.submit(
BreadCrumbLogEvent(
BreadCrumbViewUtil.getReadableScreenName(context),
"ArticleDescriptionEditing.start"
)
)
log(context, composeGroupString() + ".start")
}

fun articleDescriptionEditingEnd(context: Context) {
EventPlatformClient.submit(
BreadCrumbLogEvent(
BreadCrumbViewUtil.getReadableScreenName(context),
"ArticleDescriptionEditing.end"
)
)
log(context, composeGroupString() + ".end")
}

fun logAttempt(context: Context, finalDescription: String, wasChosen: Boolean, wasModified: Boolean, title: PageTitle) {
log(context, composeLogString(title) + ".attempt:$finalDescription" +
".suggestion1:${encode(apiOrderList.first())}" + (if (apiOrderList.size > 1) ".suggestion2:${encode(apiOrderList.last())}" else "") +
getOrderString(wasChosen, chosenSuggestion) + ".modified:$wasModified.timeSpentMs:${timer.elapsedMillis}")
}

fun logSuccess(context: Context, finalDescription: String, wasChosen: Boolean, wasModified: Boolean, title: PageTitle, revId: Long) {
log(context, composeLogString(title) + ".success:$finalDescription" +
".suggestion1:${encode(apiOrderList.first())}" + (if (apiOrderList.size > 1) ".suggestion2:${encode(apiOrderList.last())}" else "") +
getOrderString(wasChosen, chosenSuggestion) + ".modified:$wasModified.timeSpentMs:${timer.elapsedMillis}.revId:$revId")
}

fun logSuggestionsReceived(context: Context, isBlp: Boolean, title: PageTitle) {
apiFailed = false
log(context, composeLogString(title) + ".blp:$isBlp.count:${apiOrderList.size}.suggestion1:${encode(apiOrderList.first())}" +
if (apiOrderList.size > 1) ".suggestion2:${encode(apiOrderList.last())}" else "")
}

fun logSuggestionsShown(context: Context, title: PageTitle) {
log(context, composeLogString(title) + ".count:${displayOrderList.size}.display1:${encode(displayOrderList.first())}" +
if (displayOrderList.size > 1) ".display2:${encode(displayOrderList.last())}" else "")
}

fun logSuggestionChosen(context: Context, suggestion: String, title: PageTitle) {
chosenSuggestion = suggestion
log(context, composeLogString(title) + ".selected:${encode(suggestion)}${getOrderString(true, suggestion)}")
}

fun logSuggestionsDismissed(context: Context, title: PageTitle) {
log(context, composeLogString(title) + ".suggestionsDialog.dismissed")
}

fun logSuggestionReported(context: Context, suggestion: String, reportReasonsList: List<String>, title: PageTitle) {
val reportReasons = reportReasonsList.joinToString("|")
log(context, composeLogString(title) + ".reportDialog.suggestion:${encode(suggestion)}${getOrderString(true, suggestion)}.reasons:$reportReasons.reported")
}

fun logReportDialogDismissed(context: Context) {
log(context, composeGroupString() + ".reportDialog.dismissed")
}

fun logOnboardingShown(context: Context) {
log(context, composeGroupString() + ".onboardingShown")
}

fun logGroupAssigned(context: Context, testGroup: Int) {
log(context, "$MACHINE_GEN_DESC_SUGGESTIONS.groupAssigned:$testGroup")
}

fun logApiFailed(context: Context, throwable: Throwable, title: PageTitle) {
log(context, composeLogString(title) + ".apiError:${throwable.message}")
apiFailed = true
}

private fun log(context: Context, logString: String) {
if (!isUserInExperiment || apiFailed) {
return
}
EventPlatformClient.submit(BreadCrumbLogEvent(BreadCrumbViewUtil.getReadableScreenName(context), logString))
}

private fun getOrderString(wasChosen: Boolean, suggestion: String): String {
return ".chosenApiIndex:${if (!wasChosen) -1 else apiOrderList.indexOf(suggestion) + 1}" +
".chosenDisplayIndex:${if (!wasChosen) -1 else displayOrderList.indexOf(suggestion) + 1}"
}

private fun composeLogString(title: PageTitle): String {
return "${composeGroupString()}.lang:${title.wikiSite.languageCode}.title:${encode(title.prefixedText)}"
}

private fun composeGroupString(): String {
return "$MACHINE_GEN_DESC_SUGGESTIONS.group:${abcTest.group}.experienced:${Prefs.suggestedEditsMachineGeneratedDescriptionsIsExperienced}"
}

companion object {
private const val MACHINE_GEN_DESC_SUGGESTIONS = "machineSuggestions"
val abcTest = MachineGeneratedArticleDescriptionABCTest()
var isUserInExperiment = false

// HACK: We're using periods and colons as delimiting characters in these events, so let's
// urlencode just those characters, if they appear in our strings.
private fun encode(str: String): String {
return str.replace(".", "%2E").replace(":", "%3A")
}

suspend fun setUserExperienced() =
withContext(Dispatchers.Default) {
val totalContributions = ServiceFactory.get(WikipediaApp.instance.wikiSite)
.globalUserInfo(AccountUtil.userName!!).query?.globalUserInfo?.editCount ?: 0
Prefs.suggestedEditsMachineGeneratedDescriptionsIsExperienced = totalContributions > 50
}
}
}
2 changes: 1 addition & 1 deletion app/src/main/java/org/wikipedia/dataclient/Service.kt
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ interface Service {
fun getInfoByPageId(@Query("pageids") pageIds: String): Observable<MwQueryResponse>

@GET(MW_API_PREFIX + "action=query&prop=info|description|pageimages&inprop=varianttitles|displaytitle&redirects=1")
suspend fun getPageTitlesByPageId(@Query("pageids") pageIds: String): MwQueryResponse
suspend fun getPageTitlesByPageIdsOrTitles(@Query("pageids") pageIds: String? = null, @Query("titles") titles: String? = null): MwQueryResponse

@GET(MW_API_PREFIX + "action=query")
suspend fun getPageIds(@Query("titles") titles: String): MwQueryResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class MwQueryResult {
@SerialName("wikimediaeditortaskscounts") val editorTaskCounts: EditorTaskCounts? = null
@SerialName("usercontribs") val userContributions: List<UserContribution> = emptyList()
@SerialName("allusers") val allUsers: List<UserInfo>? = null
@SerialName("globaluserinfo") val globalUserInfo: UserInfo? = null

private val redirects: MutableList<Redirect>? = null
private val converted: MutableList<ConvertedTitle>? = null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.wikipedia.dataclient.wikidata

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
Expand Down Expand Up @@ -27,7 +28,7 @@ class Entities : MwResponse() {
val sitelinks: Map<String, SiteLink> = emptyMap()
val statements: JsonElement? = null
val missing: JsonElement? = null
val lastRevId: Long = 0
@SerialName("lastrevid") val lastRevId: Long = 0

fun getStatements(): Map<String, List<Claims.Claim>> {
return if (statements != null && statements !is JsonArray) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,52 @@ package org.wikipedia.descriptions

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.annotation.ColorInt
import org.wikipedia.Constants
import org.wikipedia.Constants.InvokeSource
import org.wikipedia.R
import org.wikipedia.activity.SingleFragmentActivity
import org.wikipedia.analytics.eventplatform.ABTest.Companion.GROUP_1
import org.wikipedia.analytics.eventplatform.MachineGeneratedArticleDescriptionsAnalyticsHelper
import org.wikipedia.history.HistoryEntry
import org.wikipedia.page.ExclusiveBottomSheetPresenter
import org.wikipedia.page.PageActivity
import org.wikipedia.page.PageTitle
import org.wikipedia.page.linkpreview.LinkPreviewDialog
import org.wikipedia.readinglist.AddToReadingListDialog
import org.wikipedia.settings.Prefs
import org.wikipedia.suggestededits.PageSummaryForEdit
import org.wikipedia.util.ClipboardUtil
import org.wikipedia.util.DeviceUtil
import org.wikipedia.util.FeedbackUtil
import org.wikipedia.util.ShareUtil
import org.wikipedia.views.ImagePreviewDialog
import org.wikipedia.views.SuggestedArticleDescriptionsDialog

class DescriptionEditActivity : SingleFragmentActivity<DescriptionEditFragment>(), DescriptionEditFragment.Callback, LinkPreviewDialog.Callback {
enum class Action {
ADD_DESCRIPTION, TRANSLATE_DESCRIPTION, ADD_CAPTION, TRANSLATE_CAPTION, ADD_IMAGE_TAGS
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val action = intent.getSerializableExtra(Constants.INTENT_EXTRA_ACTION) as Action
val pageTitle = intent.getParcelableExtra<PageTitle>(EXTRA_TITLE)!!

MachineGeneratedArticleDescriptionsAnalyticsHelper.isUserInExperiment = (action == Action.ADD_DESCRIPTION &&
pageTitle.description.isNullOrEmpty() &&
SuggestedArticleDescriptionsDialog.availableLanguages.contains(pageTitle.wikiSite.languageCode))

val shouldShowAIOnBoarding = MachineGeneratedArticleDescriptionsAnalyticsHelper.isUserInExperiment &&
MachineGeneratedArticleDescriptionsAnalyticsHelper.abcTest.group != GROUP_1

if (action == Action.ADD_DESCRIPTION && Prefs.isDescriptionEditTutorialEnabled) {
Prefs.isDescriptionEditTutorialEnabled = false
startActivity(DescriptionEditTutorialActivity.newIntent(this, shouldShowAIOnBoarding))
}
}

public override fun createFragment(): DescriptionEditFragment {
val invokeSource = intent.getSerializableExtra(Constants.INTENT_EXTRA_INVOKE_SOURCE) as InvokeSource
val action = intent.getSerializableExtra(Constants.INTENT_EXTRA_ACTION) as Action
Expand Down
Loading

0 comments on commit 2b57d8a

Please sign in to comment.