From d2e869ffeec4f5e07f787e05f8e626538fbcf1ce Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Mon, 5 Aug 2024 13:49:34 -0500 Subject: [PATCH 1/7] Add sprint 1 actions to EditInteractionFunnel --- Wikipedia/Code/EditInteractionFunnel.swift | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/Wikipedia/Code/EditInteractionFunnel.swift b/Wikipedia/Code/EditInteractionFunnel.swift index f0aa83ab4cf..794932c4365 100644 --- a/Wikipedia/Code/EditInteractionFunnel.swift +++ b/Wikipedia/Code/EditInteractionFunnel.swift @@ -28,6 +28,10 @@ final class EditInteractionFunnel { case articleEditPreview = "article_edit_preview" case articleEditSummary = "article_edit_summary" case talkEditSummary = "talk_edit_summary" + + // Alt-Text-Experiment Items + case altTextEditingOnboarding = "alt_text_editing_onboarding" + case altTextEditingInterface = "alt_text_editing_interface" } private enum Action: String { @@ -42,6 +46,17 @@ final class EditInteractionFunnel { case saveAttempt = "save_attempt" case saveSuccess = "save_success" case saveFailure = "save_failure" + + // Alt-Text-Experiment Items + case groupAssignment = "group_assignment" + case launchImpression = "launch_impression" + case launchCloseClick = "launch_close_click" + case addClick = "add_click" + case doNotAddClick = "do_not_add_click" + case addAltTextImpression = "add_alt_text_impression" + case addAltTextInput = "add_alt_text_input" + case altTextEditSuccess = "alt_text_edit_success" + case minimizedImpression = "minimized_impression" } private struct Event: EventInterface { @@ -236,4 +251,59 @@ final class EditInteractionFunnel { let actionData = ["abort_source": ProblemSource.blockedMessageLink.rawValue] logEvent(activeInterface: .talkEditSummary, action: .editCancel, actionData: actionData, project: project) } + + // MARK: Alt-Text-Experiment + + func logAltTextDidAssignGroupA(project: WikimediaProject) { + let actionData = ["exp_b_group": "a"] + logEvent(activeInterface: .altTextEditingOnboarding, action: .groupAssignment, actionData: actionData, project: project) + } + + func logAltTextDidAssignGroupB(project: WikimediaProject) { + let actionData = ["exp_b_group": "b"] + logEvent(activeInterface: .altTextEditingOnboarding, action: .groupAssignment, actionData: actionData, project: project) + } + + func logAltTextPromptDidAppear(project: WikimediaProject) { + logEvent(activeInterface: .altTextEditingOnboarding, action: .launchImpression, project: project) + } + + func logAltTextPromptDidTapClose(project: WikimediaProject) { + logEvent(activeInterface: .altTextEditingOnboarding, action: .launchCloseClick, project: project) + } + + func logAltTextPromptDidTapAdd(project: WikimediaProject) { + logEvent(activeInterface: .altTextEditingOnboarding, action: .addClick, project: project) + } + + func logAltTextPromptDidTapDoNotAdd(project: WikimediaProject) { + logEvent(activeInterface: .altTextEditingOnboarding, action: .doNotAddClick, project: project) + } + + func logAltTextInputDidAppear(project: WikimediaProject) { + logEvent(activeInterface: .altTextEditingInterface, action: .addAltTextImpression, project: project) + } + + func logAltTextInputDidFocus(project: WikimediaProject) { + logEvent(activeInterface: .altTextEditingInterface, action: .addAltTextInput, project: project) + } + + func logAltTextInputDidMinimize(project: WikimediaProject) { + logEvent(activeInterface: .altTextEditingInterface, action: .minimizedImpression, project: project) + } + + func logAltTextDidSuccessfullyPostEdit(timeSpent: Int, revisionID: UInt64, altText: String, articleTitle: String, image: String, username: String, userEditCount: UInt64, userCreateDateString: String, project: WikimediaProject) { + + let actionData = ["time_spent": String(timeSpent), + "revision_id": String(revisionID), + "alt_text": altText, + "article_title": articleTitle, + "image": image, + "username": username, + "event_user_revision_count": String(userEditCount), + "user_create_date": userCreateDateString] + + logEvent(activeInterface: .altTextEditingInterface, action: .altTextEditSuccess, actionData: actionData, project: project) + } } + From f41203626ad0df33e287478280ae1f03896aa734 Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Mon, 5 Aug 2024 15:39:28 -0500 Subject: [PATCH 2/7] Log experiment B group assignment --- ...WKImageRecommendationsViewController.swift | 2 ++ .../Alt Text/WKAltTextDataController.swift | 5 ++++ Wikipedia/Code/EditInteractionFunnel.swift | 24 +++++++++++++------ Wikipedia/Code/ExploreViewController.swift | 12 +++++++++- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsViewController.swift b/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsViewController.swift index abbdc420418..c7381071ce9 100644 --- a/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsViewController.swift +++ b/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsViewController.swift @@ -35,6 +35,7 @@ public protocol WKImageRecommendationsLoggingDelegate: AnyObject { func logEmptyStateDidAppear() func logEmptyStateDidTapBack() func logDialogWarningMessageDidDisplay(fileName: String, recommendationSource: String) + func logAltTextExperimentDidAssignGroup() } fileprivate final class WKImageRecommendationsHostingViewController: WKComponentHostingController { @@ -202,6 +203,7 @@ public final class WKImageRecommendationsViewController: WKCanvasViewController do { try dataController.assignImageRecsExperiment(isLoggedIn: isLoggedIn, project: viewModel.project) + loggingDelegate?.logAltTextExperimentDidAssignGroup() } catch let error { debugPrint(error) return false diff --git a/WKData/Sources/WKData/Data Controllers/Alt Text/WKAltTextDataController.swift b/WKData/Sources/WKData/Data Controllers/Alt Text/WKAltTextDataController.swift index 62e897cffe8..f2cda0e8eec 100644 --- a/WKData/Sources/WKData/Data Controllers/Alt Text/WKAltTextDataController.swift +++ b/WKData/Sources/WKData/Data Controllers/Alt Text/WKAltTextDataController.swift @@ -20,6 +20,7 @@ public final class WKAltTextDataController { case invalidDeviceOrOS case invalidDate case unexpectedBucketValue + case alreadyAssignedThisExperiment case alreadyAssignedOtherExperiment } @@ -64,6 +65,10 @@ public final class WKAltTextDataController { throw WKAltTextDataControllerError.invalidDate } + if experimentsDataController.bucketForExperiment(.altTextImageRecommendations) != nil { + throw WKAltTextDataControllerError.alreadyAssignedThisExperiment + } + if let articleEditorExperimentBucket = experimentsDataController.bucketForExperiment(.altTextArticleEditor) { switch articleEditorExperimentBucket { diff --git a/Wikipedia/Code/EditInteractionFunnel.swift b/Wikipedia/Code/EditInteractionFunnel.swift index 794932c4365..b56964ffb5f 100644 --- a/Wikipedia/Code/EditInteractionFunnel.swift +++ b/Wikipedia/Code/EditInteractionFunnel.swift @@ -1,4 +1,5 @@ import Foundation +import WKData final class EditInteractionFunnel { @@ -254,13 +255,22 @@ final class EditInteractionFunnel { // MARK: Alt-Text-Experiment - func logAltTextDidAssignGroupA(project: WikimediaProject) { - let actionData = ["exp_b_group": "a"] - logEvent(activeInterface: .altTextEditingOnboarding, action: .groupAssignment, actionData: actionData, project: project) - } - - func logAltTextDidAssignGroupB(project: WikimediaProject) { - let actionData = ["exp_b_group": "b"] + func logAltTextDidAssignImageRecsGroup(project: WikimediaProject) { + + guard let group = WKAltTextDataController.shared?.assignedAltTextImageRecommendationsGroupForLogging() else { + return + } + + var actionData: [String: String] = [:] + switch group { + case "A": + actionData["exp_b_group"] = "a" + case "B": + actionData["exp_b_group"] = "b" + default: + assertionFailure("Unexpected experiment group") + } + logEvent(activeInterface: .altTextEditingOnboarding, action: .groupAssignment, actionData: actionData, project: project) } diff --git a/Wikipedia/Code/ExploreViewController.swift b/Wikipedia/Code/ExploreViewController.swift index 147ec15f1ef..d09a1b1aa72 100644 --- a/Wikipedia/Code/ExploreViewController.swift +++ b/Wikipedia/Code/ExploreViewController.swift @@ -1512,7 +1512,17 @@ extension ExploreViewController: WKFeatureAnnouncing { } extension ExploreViewController: WKImageRecommendationsLoggingDelegate { - + func logAltTextExperimentDidAssignGroup() { + + guard let imageRecommendationsViewModel else { + return + } + + let project = WikimediaProject(wkProject: imageRecommendationsViewModel.project) + + EditInteractionFunnel.shared.logAltTextDidAssignImageRecsGroup(project: project) + } + func logOnboardingDidTapPrimaryButton() { ImageRecommendationsFunnel.shared.logOnboardingDidTapContinue() } From 173211052d048ece1a92b4258ea59e9ec8f90957 Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Mon, 5 Aug 2024 15:55:55 -0500 Subject: [PATCH 3/7] Log actions from prompt --- Wikipedia/Code/ExploreViewController.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Wikipedia/Code/ExploreViewController.swift b/Wikipedia/Code/ExploreViewController.swift index d09a1b1aa72..65d5dc50e82 100644 --- a/Wikipedia/Code/ExploreViewController.swift +++ b/Wikipedia/Code/ExploreViewController.swift @@ -1319,6 +1319,8 @@ extension ExploreViewController: WKImageRecommendationsDelegate { let sheetLocalizedStrings = AltTextExperimentModalSheetViewModel.LocalizedStrings(title: addAltTextTitle, buttonTitle: CommonStrings.nextTitle, textViewPlaceholder: textViewPlaceholder) let bottomSheetViewModel = AltTextExperimentModalSheetViewModel(altTextViewModel: altTextViewModel, localizedStrings: sheetLocalizedStrings) + + EditInteractionFunnel.shared.logAltTextPromptDidTapAdd(project: WikimediaProject(wkProject: viewModel.project)) if let siteURL = viewModel.project.siteURL, let articleURL = siteURL.wmf_URL(withTitle: articleTitle), @@ -1326,14 +1328,14 @@ extension ExploreViewController: WKImageRecommendationsDelegate { self.navigationController?.pushViewController(articleViewController, animated: true) } - - // todo: once alt text is published, bring back up this bottom sheet - // imageRecommendationsViewController.presentImageRecommendationBottomSheet() } } - let secondaryTapHandler: ScrollableEducationPanelButtonTapHandler = { [weak self] _, _ in + let secondaryTapHandler: ScrollableEducationPanelButtonTapHandler = { _, _ in imageRecommendationsViewController.dismiss(animated: true) { + + EditInteractionFunnel.shared.logAltTextPromptDidTapDoNotAdd(project: WikimediaProject(wkProject: viewModel.project)) + // show survey // once survey is done, bring back up next recommendation imageRecommendationsViewController.presentImageRecommendationBottomSheet() @@ -1345,6 +1347,7 @@ extension ExploreViewController: WKImageRecommendationsDelegate { case .tappedPrimary, .tappedSecondary: break default: + EditInteractionFunnel.shared.logAltTextPromptDidTapClose(project: WikimediaProject(wkProject: viewModel.project)) imageRecommendationsViewController.presentImageRecommendationBottomSheet() } } From 0de9c668bded4bcf77b29ee97ea376bd43494224 Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Mon, 5 Aug 2024 16:14:10 -0500 Subject: [PATCH 4/7] Add missing impression for prompt, add bottom sheet actions --- .../AltTextExperimentModalSheetView.swift | 5 ++- ...xtExperimentModalSheetViewController.swift | 15 +++++++-- Wikipedia/Code/ArticleViewController.swift | 33 ++++++++++++++++++- Wikipedia/Code/ExploreViewController.swift | 3 ++ 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/Components/Sources/Components/Components/Suggested Edits/Alt Text Experiment/AltTextExperimentModalSheetView.swift b/Components/Sources/Components/Components/Suggested Edits/Alt Text Experiment/AltTextExperimentModalSheetView.swift index a16310fee9b..4acf0ad9e59 100644 --- a/Components/Sources/Components/Components/Suggested Edits/Alt Text Experiment/AltTextExperimentModalSheetView.swift +++ b/Components/Sources/Components/Components/Suggested Edits/Alt Text Experiment/AltTextExperimentModalSheetView.swift @@ -6,6 +6,7 @@ final class AltTextExperimentModalSheetView: WKComponentView { weak var viewModel: AltTextExperimentModalSheetViewModel? weak var delegate: AltTextExperimentModalSheetDelegate? + weak var loggingDelegate: AltTextExperimentModalSheetLoggingDelegate? private lazy var scrollView: UIScrollView = { let scrollView = UIScrollView() @@ -99,9 +100,10 @@ final class AltTextExperimentModalSheetView: WKComponentView { // MARK: Lifecycle - public init(frame: CGRect, viewModel: AltTextExperimentModalSheetViewModel, delegate: AltTextExperimentModalSheetDelegate?) { + public init(frame: CGRect, viewModel: AltTextExperimentModalSheetViewModel, delegate: AltTextExperimentModalSheetDelegate?, loggingDelegate: AltTextExperimentModalSheetLoggingDelegate?) { self.viewModel = viewModel self.delegate = delegate + self.loggingDelegate = loggingDelegate super.init(frame: frame) textView.delegate = self setup() @@ -222,6 +224,7 @@ extension AltTextExperimentModalSheetView: UITextViewDelegate { func textViewDidBeginEditing(_ textView: UITextView) { placeholder.isHidden = true + loggingDelegate?.didFocusTextView() } func textViewDidEndEditing(_ textView: UITextView) { diff --git a/Components/Sources/Components/Components/Suggested Edits/Alt Text Experiment/AltTextExperimentModalSheetViewController.swift b/Components/Sources/Components/Components/Suggested Edits/Alt Text Experiment/AltTextExperimentModalSheetViewController.swift index 05554e9537c..cd12a403fae 100644 --- a/Components/Sources/Components/Components/Suggested Edits/Alt Text Experiment/AltTextExperimentModalSheetViewController.swift +++ b/Components/Sources/Components/Components/Suggested Edits/Alt Text Experiment/AltTextExperimentModalSheetViewController.swift @@ -4,14 +4,21 @@ public protocol AltTextExperimentModalSheetDelegate: AnyObject { func didTapNext(altText: String) } +public protocol AltTextExperimentModalSheetLoggingDelegate: AnyObject { + func didAppear() + func didFocusTextView() +} + final public class AltTextExperimentModalSheetViewController: WKCanvasViewController { weak var viewModel: AltTextExperimentModalSheetViewModel? weak var delegate: AltTextExperimentModalSheetDelegate? + weak var loggingDelegate: AltTextExperimentModalSheetLoggingDelegate? - public init(viewModel: AltTextExperimentModalSheetViewModel?, delegate: AltTextExperimentModalSheetDelegate?) { + public init(viewModel: AltTextExperimentModalSheetViewModel?, delegate: AltTextExperimentModalSheetDelegate?, loggingDelegate: AltTextExperimentModalSheetLoggingDelegate?) { self.viewModel = viewModel self.delegate = delegate + self.loggingDelegate = loggingDelegate super.init() } @@ -22,9 +29,13 @@ final public class AltTextExperimentModalSheetViewController: WKCanvasViewContro override public func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) guard let viewModel else { return } - let view = AltTextExperimentModalSheetView(frame: UIScreen.main.bounds, viewModel: viewModel, delegate: delegate) + let view = AltTextExperimentModalSheetView(frame: UIScreen.main.bounds, viewModel: viewModel, delegate: delegate, loggingDelegate: loggingDelegate) addComponent(view, pinToEdges: true) } + public override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + loggingDelegate?.didAppear() + } } diff --git a/Wikipedia/Code/ArticleViewController.swift b/Wikipedia/Code/ArticleViewController.swift index a5e0939d9af..961c034e9d8 100644 --- a/Wikipedia/Code/ArticleViewController.swift +++ b/Wikipedia/Code/ArticleViewController.swift @@ -493,7 +493,7 @@ class ArticleViewController: ViewController, HintPresenting { messagingController.hideEditPencils() messagingController.scrollToNewImage(filename: altTextExperimentViewModel.filename) - let bottomSheetViewController = AltTextExperimentModalSheetViewController(viewModel: altTextBottomSheetViewModel, delegate: self) + let bottomSheetViewController = AltTextExperimentModalSheetViewController(viewModel: altTextBottomSheetViewModel, delegate: self, loggingDelegate: self) if #available(iOS 16.0, *) { if let sheet = bottomSheetViewController.sheetPresentationController { @@ -1389,8 +1389,39 @@ extension ArticleViewController: UISheetPresentationControllerDelegate { case .medium, .large: webView.scrollView.contentInset = UIEdgeInsets(top: oldContentInset.top, left: oldContentInset.left, bottom: view.bounds.height * 0.65, right: oldContentInset.right) default: + logMinimized() webView.scrollView.contentInset = UIEdgeInsets(top: oldContentInset.top, left: oldContentInset.left, bottom: 75, right: oldContentInset.right) } } } + + private func logMinimized() { + guard let siteURL = articleURL.wmf_site, + let project = WikimediaProject(siteURL: siteURL) else { + return + } + + EditInteractionFunnel.shared.logAltTextInputDidMinimize(project: project) + } +} + +extension ArticleViewController: AltTextExperimentModalSheetLoggingDelegate { + func didAppear() { + + guard let siteURL = articleURL.wmf_site, + let project = WikimediaProject(siteURL: siteURL) else { + return + } + + EditInteractionFunnel.shared.logAltTextInputDidAppear(project: project) + } + + func didFocusTextView() { + guard let siteURL = articleURL.wmf_site, + let project = WikimediaProject(siteURL: siteURL) else { + return + } + + EditInteractionFunnel.shared.logAltTextInputDidFocus(project: project) + } } diff --git a/Wikipedia/Code/ExploreViewController.swift b/Wikipedia/Code/ExploreViewController.swift index 65d5dc50e82..fc071f76e64 100644 --- a/Wikipedia/Code/ExploreViewController.swift +++ b/Wikipedia/Code/ExploreViewController.swift @@ -1353,6 +1353,9 @@ extension ExploreViewController: WKImageRecommendationsDelegate { } let panel = AltTextExperimentPanelViewController(showCloseButton: true, buttonStyle: .updatedStyle, primaryButtonTapHandler: primaryTapHandler, secondaryButtonTapHandler: secondaryTapHandler, traceableDismissHandler: traceableDismissHandler, theme: self.theme, isFlowB: isFlowB) + + EditInteractionFunnel.shared.logAltTextPromptDidAppear(project: WikimediaProject(wkProject: viewModel.project)) + imageRecommendationsViewController.present(panel, animated: true) let dataController = WKAltTextDataController.shared dataController?.markSawAltTextImageRecommendationsPrompt() From 857c15141ffe2c2ed8b0fab854db0c13a38cb668 Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Mon, 5 Aug 2024 17:09:11 -0500 Subject: [PATCH 5/7] Log edit success action --- .../WKImageRecommendationsViewModel.swift | 1 + Wikipedia/Code/EditInteractionFunnel.swift | 11 ++++-- Wikipedia/Code/ExploreViewController.swift | 39 ++++++++++++++++--- Wikipedia/Code/WMFAuthenticationManager.swift | 7 ++++ .../WMFCurrentlyLoggedInUserFetcher.swift | 9 +++-- 5 files changed, 54 insertions(+), 13 deletions(-) diff --git a/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsViewModel.swift b/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsViewModel.swift index e87fccdd7ff..184e005d872 100644 --- a/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsViewModel.swift +++ b/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsViewModel.swift @@ -123,6 +123,7 @@ public final class WKImageRecommendationsViewModel: ObservableObject { public var imageWikitext: String? public var fullArticleWikitextWithImage: String? public var suggestionAcceptDate: Date? + public var altTextExperimentAcceptDate: Date? public var lastRevisionID: UInt64? public var localizedFileTitle: String? diff --git a/Wikipedia/Code/EditInteractionFunnel.swift b/Wikipedia/Code/EditInteractionFunnel.swift index b56964ffb5f..a5931a6b483 100644 --- a/Wikipedia/Code/EditInteractionFunnel.swift +++ b/Wikipedia/Code/EditInteractionFunnel.swift @@ -302,16 +302,19 @@ final class EditInteractionFunnel { logEvent(activeInterface: .altTextEditingInterface, action: .minimizedImpression, project: project) } - func logAltTextDidSuccessfullyPostEdit(timeSpent: Int, revisionID: UInt64, altText: String, articleTitle: String, image: String, username: String, userEditCount: UInt64, userCreateDateString: String, project: WikimediaProject) { + func logAltTextDidSuccessfullyPostEdit(timeSpent: Int, revisionID: UInt64, altText: String, articleTitle: String, image: String, username: String, userEditCount: UInt64, registrationDate: String?, project: WikimediaProject) { - let actionData = ["time_spent": String(timeSpent), + var actionData = ["time_spent": String(timeSpent), "revision_id": String(revisionID), "alt_text": altText, "article_title": articleTitle, "image": image, "username": username, - "event_user_revision_count": String(userEditCount), - "user_create_date": userCreateDateString] + "event_user_revision_count": String(userEditCount)] + + if let registrationDate { + actionData["user_create_date"] = registrationDate + } logEvent(activeInterface: .altTextEditingInterface, action: .altTextEditSuccess, actionData: actionData, project: project) } diff --git a/Wikipedia/Code/ExploreViewController.swift b/Wikipedia/Code/ExploreViewController.swift index fc071f76e64..13e09756123 100644 --- a/Wikipedia/Code/ExploreViewController.swift +++ b/Wikipedia/Code/ExploreViewController.swift @@ -1320,6 +1320,8 @@ extension ExploreViewController: WKImageRecommendationsDelegate { let bottomSheetViewModel = AltTextExperimentModalSheetViewModel(altTextViewModel: altTextViewModel, localizedStrings: sheetLocalizedStrings) + lastRecommendation.altTextExperimentAcceptDate = Date() + EditInteractionFunnel.shared.logAltTextPromptDidTapAdd(project: WikimediaProject(wkProject: viewModel.project)) if let siteURL = viewModel.project.siteURL, @@ -1749,13 +1751,15 @@ extension ExploreViewController: AltTextDelegate { } let developerSettings = WKDeveloperSettingsDataController() + if viewModel.isFlowB && developerSettings.doNotPostImageRecommendationsEdit { navigationController?.popViewController(animated: true) // wait for animation to complete - DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { - self.presentAltTextEditPublishedToast() + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in + self?.presentAltTextEditPublishedToast() + self?.logAltTextEditSuccess(altText: altText, revisionID: 0) } return @@ -1775,17 +1779,40 @@ extension ExploreViewController: AltTextDelegate { self.navigationController?.popViewController(animated: true) - // wait for animation to complete + guard let fetchedData = result as? [String: Any], + let newRevID = fetchedData["newrevid"] as? UInt64 else { + return + } + if error == nil { - DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { - self.presentAltTextEditPublishedToast() + // wait for animation to complete + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in + self?.presentAltTextEditPublishedToast() + self?.logAltTextEditSuccess(altText: altText, revisionID: newRevID) } } } } } + } + + private func logAltTextEditSuccess(altText: String, revisionID: UInt64) { - + guard let imageRecommendationsViewModel, + let lastRecommendation = imageRecommendationsViewModel.lastRecommendation, let acceptDate = lastRecommendation.altTextExperimentAcceptDate, + let siteURL = imageRecommendationsViewModel.project.siteURL else { + return + } + + let articleTitle = lastRecommendation.title + let image = lastRecommendation.imageData.filename + let timeSpent = Int(Date().timeIntervalSince(acceptDate)) + + guard let loggedInUser = dataStore.authenticationManager.getLoggedInUserCache(for: siteURL) else { + return + } + + EditInteractionFunnel.shared.logAltTextDidSuccessfullyPostEdit(timeSpent: timeSpent, revisionID: revisionID, altText: altText, articleTitle: articleTitle, image: image, username: loggedInUser.name, userEditCount: loggedInUser.editCount, registrationDate: loggedInUser.registrationDateString, project: WikimediaProject(wkProject: imageRecommendationsViewModel.project)) } private func presentAltTextEditPublishedToast() { diff --git a/Wikipedia/Code/WMFAuthenticationManager.swift b/Wikipedia/Code/WMFAuthenticationManager.swift index a8c9c7205f0..6239602bf2c 100644 --- a/Wikipedia/Code/WMFAuthenticationManager.swift +++ b/Wikipedia/Code/WMFAuthenticationManager.swift @@ -63,6 +63,13 @@ import CocoaLumberjackSwift private var isAnonCache: [String: Bool] = [:] private var loggedInUserCache: [String: WMFCurrentlyLoggedInUser] = [:] + public func getLoggedInUserCache(for siteURL: URL) -> WMFCurrentlyLoggedInUser? { + guard let host = siteURL.host else { + return nil + } + return loggedInUserCache[host] + } + @objc func getLoggedInUser(for siteURL: URL, completion: @escaping (WMFCurrentlyLoggedInUser?) -> Void) { getLoggedInUser(for: siteURL) { result in switch result { diff --git a/Wikipedia/Code/WMFCurrentlyLoggedInUserFetcher.swift b/Wikipedia/Code/WMFCurrentlyLoggedInUserFetcher.swift index dd03e87f49e..e3827eecb8d 100644 --- a/Wikipedia/Code/WMFCurrentlyLoggedInUserFetcher.swift +++ b/Wikipedia/Code/WMFCurrentlyLoggedInUserFetcher.swift @@ -22,12 +22,14 @@ public typealias WMFCurrentlyLoggedInUserBlock = (WMFCurrentlyLoggedInUser) -> V @objc public var groups: [String] @objc public var editCount: UInt64 @objc public var isBlocked: Bool - init(userID: Int, name: String, groups: [String], editCount: UInt64, isBlocked: Bool) { + @objc public var registrationDateString: String? + init(userID: Int, name: String, groups: [String], editCount: UInt64, isBlocked: Bool, registrationDateString: String?) { self.userID = userID self.name = name self.groups = groups self.editCount = editCount self.isBlocked = isBlocked + self.registrationDateString = registrationDateString } } @@ -36,7 +38,7 @@ public class WMFCurrentlyLoggedInUserFetcher: Fetcher { let parameters = [ "action": "query", "meta": "userinfo", - "uiprop": "groups|blockinfo|editcount", + "uiprop": "groups|blockinfo|editcount|registrationdate", "format": "json" ] @@ -60,6 +62,7 @@ public class WMFCurrentlyLoggedInUserFetcher: Fetcher { } let editCount = userinfo["editcount"] as? UInt64 ?? 0 + let registrationDateString = userinfo["registrationdate"] as? String var isBlocked = false if userinfo["blockid"] is UInt64 { @@ -70,7 +73,7 @@ public class WMFCurrentlyLoggedInUserFetcher: Fetcher { } let groups = userinfo["groups"] as? [String] ?? [] - success(WMFCurrentlyLoggedInUser.init(userID: userID, name: userName, groups: groups, editCount: editCount, isBlocked: isBlocked)) + success(WMFCurrentlyLoggedInUser.init(userID: userID, name: userName, groups: groups, editCount: editCount, isBlocked: isBlocked, registrationDateString: registrationDateString)) } } } From e696212c8fe254873b869897b3412bfda768ebb7 Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Tue, 6 Aug 2024 09:49:53 -0500 Subject: [PATCH 6/7] Fix bugs after merge --- ...ltTextExperimentModalSheetViewController.swift | 15 +++++++++++++-- .../Alt Text/WMFAltTextDataController.swift | 2 +- Wikipedia/Code/ArticleViewController.swift | 2 +- Wikipedia/Code/EditInteractionFunnel.swift | 4 ++-- Wikipedia/Code/ExploreViewController.swift | 12 ++++++------ 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/WMFComponents/Sources/WMFComponents/Components/Suggested Edits/Alt Text Experiment/WMFAltTextExperimentModalSheetViewController.swift b/WMFComponents/Sources/WMFComponents/Components/Suggested Edits/Alt Text Experiment/WMFAltTextExperimentModalSheetViewController.swift index cc01c46e114..209731ca3ad 100644 --- a/WMFComponents/Sources/WMFComponents/Components/Suggested Edits/Alt Text Experiment/WMFAltTextExperimentModalSheetViewController.swift +++ b/WMFComponents/Sources/WMFComponents/Components/Suggested Edits/Alt Text Experiment/WMFAltTextExperimentModalSheetViewController.swift @@ -4,14 +4,21 @@ public protocol WMFAltTextExperimentModalSheetDelegate: AnyObject { func didTapNext(altText: String) } +public protocol WMFAltTextExperimentModalSheetLoggingDelegate: AnyObject { + func didAppear() + func didFocusTextView() +} + final public class WMFAltTextExperimentModalSheetViewController: WMFCanvasViewController { weak var viewModel: WMFAltTextExperimentModalSheetViewModel? weak var delegate: WMFAltTextExperimentModalSheetDelegate? + weak var loggingDelegate: WMFAltTextExperimentModalSheetLoggingDelegate? - public init(viewModel: WMFAltTextExperimentModalSheetViewModel?, delegate: WMFAltTextExperimentModalSheetDelegate?) { + public init(viewModel: WMFAltTextExperimentModalSheetViewModel?, delegate: WMFAltTextExperimentModalSheetDelegate?, loggingDelegate: WMFAltTextExperimentModalSheetLoggingDelegate?) { self.viewModel = viewModel self.delegate = delegate + self.loggingDelegate = loggingDelegate super.init() } @@ -22,9 +29,13 @@ final public class WMFAltTextExperimentModalSheetViewController: WMFCanvasViewCo override public func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) guard let viewModel else { return } - let view = WMFAltTextExperimentModalSheetView(frame: UIScreen.main.bounds, viewModel: viewModel, delegate: delegate) + let view = WMFAltTextExperimentModalSheetView(frame: UIScreen.main.bounds, viewModel: viewModel, delegate: delegate, loggingDelegate: loggingDelegate) addComponent(view, pinToEdges: true) } + public override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + loggingDelegate?.didAppear() + } } diff --git a/WMFData/Sources/WMFData/Data Controllers/Alt Text/WMFAltTextDataController.swift b/WMFData/Sources/WMFData/Data Controllers/Alt Text/WMFAltTextDataController.swift index b4864164c56..38026f036a6 100644 --- a/WMFData/Sources/WMFData/Data Controllers/Alt Text/WMFAltTextDataController.swift +++ b/WMFData/Sources/WMFData/Data Controllers/Alt Text/WMFAltTextDataController.swift @@ -66,7 +66,7 @@ public final class WMFAltTextDataController { } if experimentsDataController.bucketForExperiment(.altTextImageRecommendations) != nil { - throw WKAltTextDataControllerError.alreadyAssignedThisExperiment + throw WMFAltTextDataControllerError.alreadyAssignedThisExperiment } if let articleEditorExperimentBucket = experimentsDataController.bucketForExperiment(.altTextArticleEditor) { diff --git a/Wikipedia/Code/ArticleViewController.swift b/Wikipedia/Code/ArticleViewController.swift index d3ac90266b8..4fb1be963e9 100644 --- a/Wikipedia/Code/ArticleViewController.swift +++ b/Wikipedia/Code/ArticleViewController.swift @@ -1405,7 +1405,7 @@ extension ArticleViewController: UISheetPresentationControllerDelegate { } } -extension ArticleViewController: AltTextExperimentModalSheetLoggingDelegate { +extension ArticleViewController: WMFAltTextExperimentModalSheetLoggingDelegate { func didAppear() { guard let siteURL = articleURL.wmf_site, diff --git a/Wikipedia/Code/EditInteractionFunnel.swift b/Wikipedia/Code/EditInteractionFunnel.swift index a5931a6b483..1c16bd6510e 100644 --- a/Wikipedia/Code/EditInteractionFunnel.swift +++ b/Wikipedia/Code/EditInteractionFunnel.swift @@ -1,5 +1,5 @@ import Foundation -import WKData +import WMFData final class EditInteractionFunnel { @@ -257,7 +257,7 @@ final class EditInteractionFunnel { func logAltTextDidAssignImageRecsGroup(project: WikimediaProject) { - guard let group = WKAltTextDataController.shared?.assignedAltTextImageRecommendationsGroupForLogging() else { + guard let group = WMFAltTextDataController.shared?.assignedAltTextImageRecommendationsGroupForLogging() else { return } diff --git a/Wikipedia/Code/ExploreViewController.swift b/Wikipedia/Code/ExploreViewController.swift index d2706cdcddf..41036961ccd 100644 --- a/Wikipedia/Code/ExploreViewController.swift +++ b/Wikipedia/Code/ExploreViewController.swift @@ -1322,7 +1322,7 @@ extension ExploreViewController: WMFImageRecommendationsDelegate { lastRecommendation.altTextExperimentAcceptDate = Date() - EditInteractionFunnel.shared.logAltTextPromptDidTapAdd(project: WikimediaProject(wkProject: viewModel.project)) + EditInteractionFunnel.shared.logAltTextPromptDidTapAdd(project: WikimediaProject(wmfProject: viewModel.project)) if let siteURL = viewModel.project.siteURL, let articleURL = siteURL.wmf_URL(withTitle: articleTitle), @@ -1336,7 +1336,7 @@ extension ExploreViewController: WMFImageRecommendationsDelegate { let secondaryTapHandler: ScrollableEducationPanelButtonTapHandler = { _, _ in imageRecommendationsViewController.dismiss(animated: true) { - EditInteractionFunnel.shared.logAltTextPromptDidTapDoNotAdd(project: WikimediaProject(wkProject: viewModel.project)) + EditInteractionFunnel.shared.logAltTextPromptDidTapDoNotAdd(project: WikimediaProject(wmfProject: viewModel.project)) // show survey // once survey is done, bring back up next recommendation @@ -1349,14 +1349,14 @@ extension ExploreViewController: WMFImageRecommendationsDelegate { case .tappedPrimary, .tappedSecondary: break default: - EditInteractionFunnel.shared.logAltTextPromptDidTapClose(project: WikimediaProject(wkProject: viewModel.project)) + EditInteractionFunnel.shared.logAltTextPromptDidTapClose(project: WikimediaProject(wmfProject: viewModel.project)) imageRecommendationsViewController.presentImageRecommendationBottomSheet() } } let panel = AltTextExperimentPanelViewController(showCloseButton: true, buttonStyle: .updatedStyle, primaryButtonTapHandler: primaryTapHandler, secondaryButtonTapHandler: secondaryTapHandler, traceableDismissHandler: traceableDismissHandler, theme: self.theme, isFlowB: isFlowB) - EditInteractionFunnel.shared.logAltTextPromptDidAppear(project: WikimediaProject(wkProject: viewModel.project)) + EditInteractionFunnel.shared.logAltTextPromptDidAppear(project: WikimediaProject(wmfProject: viewModel.project)) imageRecommendationsViewController.present(panel, animated: true) let dataController = WMFAltTextDataController.shared @@ -1526,7 +1526,7 @@ extension ExploreViewController: WMFImageRecommendationsLoggingDelegate { return } - let project = WikimediaProject(wkProject: imageRecommendationsViewModel.project) + let project = WikimediaProject(wmfProject: imageRecommendationsViewModel.project) EditInteractionFunnel.shared.logAltTextDidAssignImageRecsGroup(project: project) } @@ -1812,7 +1812,7 @@ extension ExploreViewController: AltTextDelegate { return } - EditInteractionFunnel.shared.logAltTextDidSuccessfullyPostEdit(timeSpent: timeSpent, revisionID: revisionID, altText: altText, articleTitle: articleTitle, image: image, username: loggedInUser.name, userEditCount: loggedInUser.editCount, registrationDate: loggedInUser.registrationDateString, project: WikimediaProject(wkProject: imageRecommendationsViewModel.project)) + EditInteractionFunnel.shared.logAltTextDidSuccessfullyPostEdit(timeSpent: timeSpent, revisionID: revisionID, altText: altText, articleTitle: articleTitle, image: image, username: loggedInUser.name, userEditCount: loggedInUser.editCount, registrationDate: loggedInUser.registrationDateString, project: WikimediaProject(wmfProject: imageRecommendationsViewModel.project)) } private func presentAltTextEditPublishedToast() { From 6648ffe0c1a38d2a55976fd3ebd1c229d55be24c Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Tue, 6 Aug 2024 10:56:13 -0500 Subject: [PATCH 7/7] Log assignment for Flow C --- .../Alt Text/WMFAltTextDataController.swift | 4 ++++ .../Code/ArticleViewController+Editing.swift | 1 + Wikipedia/Code/EditInteractionFunnel.swift | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/WMFData/Sources/WMFData/Data Controllers/Alt Text/WMFAltTextDataController.swift b/WMFData/Sources/WMFData/Data Controllers/Alt Text/WMFAltTextDataController.swift index 38026f036a6..50002a92810 100644 --- a/WMFData/Sources/WMFData/Data Controllers/Alt Text/WMFAltTextDataController.swift +++ b/WMFData/Sources/WMFData/Data Controllers/Alt Text/WMFAltTextDataController.swift @@ -107,6 +107,10 @@ public final class WMFAltTextDataController { throw WMFAltTextDataControllerError.invalidDate } + if experimentsDataController.bucketForExperiment(.altTextArticleEditor) != nil { + throw WMFAltTextDataControllerError.alreadyAssignedThisExperiment + } + if let imageRecommendationsExperimentBucket = experimentsDataController.bucketForExperiment(.altTextImageRecommendations) { switch imageRecommendationsExperimentBucket { diff --git a/Wikipedia/Code/ArticleViewController+Editing.swift b/Wikipedia/Code/ArticleViewController+Editing.swift index b908c32ed49..108a4a023d1 100644 --- a/Wikipedia/Code/ArticleViewController+Editing.swift +++ b/Wikipedia/Code/ArticleViewController+Editing.swift @@ -275,6 +275,7 @@ extension ArticleViewController: EditorViewControllerDelegate { do { try dataController.assignArticleEditorExperiment(isLoggedIn: isLoggedIn, project: project) + EditInteractionFunnel.shared.logAltTextDidAssignArticleEditorGroup(project: WikimediaProject(wmfProject: project)) } catch let error { DDLogWarn("Error assigning alt text article editor experiment: \(error)") } diff --git a/Wikipedia/Code/EditInteractionFunnel.swift b/Wikipedia/Code/EditInteractionFunnel.swift index 1c16bd6510e..0fcdb192a67 100644 --- a/Wikipedia/Code/EditInteractionFunnel.swift +++ b/Wikipedia/Code/EditInteractionFunnel.swift @@ -274,6 +274,25 @@ final class EditInteractionFunnel { logEvent(activeInterface: .altTextEditingOnboarding, action: .groupAssignment, actionData: actionData, project: project) } + func logAltTextDidAssignArticleEditorGroup(project: WikimediaProject) { + + guard let group = WMFAltTextDataController.shared?.assignedAltTextArticleEditorGroupForLogging() else { + return + } + + var actionData: [String: String] = [:] + switch group { + case "C": + actionData["exp_c_group"] = "c" + case "D": + actionData["exp_c_group"] = "d" + default: + assertionFailure("Unexpected experiment group") + } + + logEvent(activeInterface: .altTextEditingOnboarding, action: .groupAssignment, actionData: actionData, project: project) + } + func logAltTextPromptDidAppear(project: WikimediaProject) { logEvent(activeInterface: .altTextEditingOnboarding, action: .launchImpression, project: project) }