Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

End of Year: sync Listening History #483

Merged
merged 25 commits into from
Nov 7, 2022

Conversation

leandroalonso
Copy link
Member

@leandroalonso leandroalonso commented Nov 4, 2022

📘 Project: #376

If we identify the user doesn't have the entire 2022 listening history, we sync this with the server adding the missing episodes.

This is a case that might happen for a very active user that switched devices during this year — given we only sync the latest 100 items.

How do we identify if a user needs a sync?

We look at their listening history. If they have an episode from last year, we assume that their 2022 listening history is complete.

A much more bulletproof strategy would be to call the endpoint to check the number of items for the user, and sync if needed, just once. But I'm not sure what would be the impact of such a call on the backend (@Ferdev curious to hear your thoughts about it).

We request the number of items the server has. If the returned number is smaller than what we have locally, we sync the necessary items.

To test

You'll need an account with more than 100 items. Please ping me and I can provide one.

  1. Enable the endOfYear flag in FeatureFlag.swift
  2. Do a clean run of the app in Staging mode (Schemes > pocketcasts > change "Build Configuration" to Staging)
  3. Login with an account with more than 100 items
  4. Check your listening history, you should see just ~40 items
  5. Put a breakpoint on EndOfYearStoriesBuilder.swift line 39
  6. Check your EoY stories
  7. ✅ The hitpoint should be hit, proceed with execution
  8. ✅ You should see the spinner for a few seconds
  9. ✅ The stories should appear
  10. Dismiss the stories
  11. Check your Listening History
  12. ✅ You should have more than 100 items there now (the oldest episode is an archived episode called "39. Mother"
  13. Check your stories again
  14. ✅ The breakpoint should not be hit (2022 sync happens just once)

Checklist

  • I have considered if this change warrants user-facing release notes and have added them to CHANGELOG.md if necessary.
  • I have considered adding unit tests for my changes.
  • I have updated (or requested that someone edit) the spreadsheet to reflect any new or changed analytics.

@leandroalonso leandroalonso added the [Project] End of Year 2023 End of Year project label Nov 4, 2022
@leandroalonso leandroalonso added this to the Future milestone Nov 4, 2022
@@ -20,7 +20,7 @@ class EndOfYearDataManager {
let query = """
SELECT playedUpTo from \(DataManager.episodeTableName)
WHERE
playedUpTo > 1800 AND
playedUpTo > 300 AND
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user had listened to multiple episodes with less than 30 minutes they were not eligible, I changed that to just 5 minutes.


/// Helper that checks for podcast existence
/// It caches database requests
class PodcastExistHelper {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created this to avoid calling the database over and over to check if an episode exists.

@leandroalonso leandroalonso marked this pull request as ready for review November 4, 2022 20:33
@leandroalonso leandroalonso requested a review from a team as a code owner November 4, 2022 20:34
@leandroalonso leandroalonso mentioned this pull request Nov 4, 2022
36 tasks
@Ferdev
Copy link

Ferdev commented Nov 7, 2022

Hey @leandroalonso!

A much more bulletproof strategy would be to call the endpoint to check the number of items for the user, and sync if needed, just once. But I'm not sure what would be the impact of such a call on the backend (@Ferdev curious to hear your thoughts about it)

I don't think it would be a problem if we just call it once. The database query is pretty simple and the whole request doesn't execute any crazy calculations, so we should be fine if we just run it one time 👍 .

Copy link
Contributor

@emilylaguna emilylaguna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heya! This worked well in my testing. I left some comments, let me know what you think.

var dataToSync = Api_YearHistoryRequest()
dataToSync.deviceTime = TimeFormatter.currentUTCTimeInMillis()
dataToSync.version = apiVersion
dataToSync.year = 2022
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about allowing this to be injected?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 9001df4

dispatchGroup.wait()

// Sync episode status for the retrieved podcasts' episodes
let uniqueUuidsToUpdate = Array(Set(podcastsToUpdate))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of converting the array -> set -> array. What do you think about just changing podcastsToUpdate to a Set?

var podcastsToUpdate: Set<String> = []

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in fecb93b

Comment on lines 143 to 145
func markAsExistent(uuid: String) {
checkedUuidsThatExist.append(uuid)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to be used, should we remove it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 194ddec


/// Helper that checks for podcast existence
/// It caches database requests
class PodcastExistHelper {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
class PodcastExistHelper {
class PodcastExistsHelper {

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 21f03cb

do {
let data = try dataToSync.serializedData()
let (response, httpStatus) = postToServer(url: url, token: token, data: data)
if let response = response, httpStatus == ServerConstants.HttpConstants.ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
if let response = response, httpStatus == ServerConstants.HttpConstants.ok {
if let response, httpStatus == ServerConstants.HttpConstants.ok {

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in fdd751b

@leandroalonso leandroalonso merged commit 8d8bf5d into trunk Nov 7, 2022
@leandroalonso leandroalonso deleted the task/376-sync-listening-history branch November 7, 2022 19:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Project] End of Year 2023 End of Year project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants