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

Stats - File downloads stats card #10411

Merged
merged 11 commits into from Aug 22, 2019
@@ -3,6 +3,7 @@
* Fix issue where establishing LinkedIn connection would always fail
* Display author of each post on Blog Posts screen
* Moved Notifications settings to Notifications List.
* File downloads in Stats for WP.com sites


13.1

This file was deleted.

@@ -13,6 +13,7 @@
GEOVIEWS,
AUTHORS,
VIDEO_PLAYS,
FILE_DOWNLOADS,
COMMENTS,
TAGS_AND_CATEGORIES,
PUBLICIZE,
@@ -29,6 +29,7 @@ sealed class NavigationTarget {
data class ViewVideoPlays(val statsGranularity: StatsGranularity, val selectedDate: Date) : NavigationTarget()
data class ViewSearchTerms(val statsGranularity: StatsGranularity, val selectedDate: Date) : NavigationTarget()
data class ViewAuthors(val statsGranularity: StatsGranularity, val selectedDate: Date) : NavigationTarget()
data class ViewFileDownloads(val statsGranularity: StatsGranularity, val selectedDate: Date) : NavigationTarget()
data class ViewUrl(val url: String) : NavigationTarget()
object ViewMonthsAndYearsStats : NavigationTarget()
object ViewDayAverageStats : NavigationTarget()
@@ -28,6 +28,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.granular.GranularUs
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.AuthorsUseCase.AuthorsUseCaseFactory
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.ClicksUseCase.ClicksUseCaseFactory
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.CountryViewsUseCase.CountryViewsUseCaseFactory
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.FileDownloadsUseCase.FileDownloadsUseCaseFactory
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.OverviewUseCase.OverviewUseCaseFactory
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.PostsAndPagesUseCase.PostsAndPagesUseCaseFactory
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.ReferrersUseCase.ReferrersUseCaseFactory
@@ -150,7 +151,8 @@ class StatsModule {
videoPlaysUseCaseFactory: VideoPlaysUseCaseFactory,
searchTermsUseCaseFactory: SearchTermsUseCaseFactory,
authorsUseCaseFactory: AuthorsUseCaseFactory,
overviewUseCaseFactory: OverviewUseCaseFactory
overviewUseCaseFactory: OverviewUseCaseFactory,
fileDownloadsUseCaseFactory: FileDownloadsUseCaseFactory
): List<@JvmSuppressWildcards GranularUseCaseFactory> {
return listOf(
postsAndPagesUseCaseFactory,
@@ -160,7 +162,8 @@ class StatsModule {
videoPlaysUseCaseFactory,
searchTermsUseCaseFactory,
authorsUseCaseFactory,
overviewUseCaseFactory
overviewUseCaseFactory,
fileDownloadsUseCaseFactory
)
}

@@ -232,7 +235,7 @@ class StatsModule {
mainDispatcher,
statsSiteProvider,
useCasesFactories.map { it.build(DAYS, BLOCK) },
{ statsStore.getTimeStatsTypes() },
{ statsStore.getTimeStatsTypes(it) },
uiModelMapper::mapTimeStats
)
}
@@ -257,7 +260,7 @@ class StatsModule {
mainDispatcher,
statsSiteProvider,
useCasesFactories.map { it.build(WEEKS, BLOCK) },
{ statsStore.getTimeStatsTypes() },
{ statsStore.getTimeStatsTypes(it) },
uiModelMapper::mapTimeStats
)
}
@@ -281,7 +284,7 @@ class StatsModule {
bgDispatcher, mainDispatcher,
statsSiteProvider,
useCasesFactories.map { it.build(MONTHS, BLOCK) },
{ statsStore.getTimeStatsTypes() },
{ statsStore.getTimeStatsTypes(it) },
uiModelMapper::mapTimeStats
)
}
@@ -306,7 +309,7 @@ class StatsModule {
mainDispatcher,
statsSiteProvider,
useCasesFactories.map { it.build(YEARS, BLOCK) },
{ statsStore.getTimeStatsTypes() },
{ statsStore.getTimeStatsTypes(it) },
uiModelMapper::mapTimeStats
)
}
@@ -15,6 +15,7 @@ import org.wordpress.android.ui.stats.StatsViewType.CLICKS
import org.wordpress.android.ui.stats.StatsViewType.DETAIL_AVERAGE_VIEWS_PER_DAY
import org.wordpress.android.ui.stats.StatsViewType.DETAIL_MONTHS_AND_YEARS
import org.wordpress.android.ui.stats.StatsViewType.DETAIL_RECENT_WEEKS
import org.wordpress.android.ui.stats.StatsViewType.FILE_DOWNLOADS
import org.wordpress.android.ui.stats.StatsViewType.FOLLOWERS
import org.wordpress.android.ui.stats.StatsViewType.GEOVIEWS
import org.wordpress.android.ui.stats.StatsViewType.INSIGHTS_ALL_TIME
@@ -38,6 +39,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.granular.GranularUs
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.AuthorsUseCase.AuthorsUseCaseFactory
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.ClicksUseCase.ClicksUseCaseFactory
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.CountryViewsUseCase.CountryViewsUseCaseFactory
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.FileDownloadsUseCase.FileDownloadsUseCaseFactory
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.PostsAndPagesUseCase.PostsAndPagesUseCaseFactory
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.ReferrersUseCase.ReferrersUseCaseFactory
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.SearchTermsUseCase.SearchTermsUseCaseFactory
@@ -157,6 +159,8 @@ class StatsViewAllViewModelFactory(
.build(granularity, VIEW_ALL), R.string.stats_view_search_terms)
VIDEO_PLAYS -> Pair(granularFactories.first { it is VideoPlaysUseCaseFactory }
.build(granularity, VIEW_ALL), R.string.stats_view_videos)
FILE_DOWNLOADS -> Pair(granularFactories.first { it is FileDownloadsUseCaseFactory }
.build(granularity, VIEW_ALL), R.string.stats_file_downloads)
else -> throw InvalidParameterException("Invalid granular stats type: ${type.name}")
}
}
@@ -0,0 +1,177 @@
package org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases

import kotlinx.coroutines.CoroutineDispatcher
import org.wordpress.android.R
import org.wordpress.android.analytics.AnalyticsTracker
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.model.stats.LimitMode
import org.wordpress.android.fluxc.model.stats.time.FileDownloadsModel
import org.wordpress.android.fluxc.network.utils.StatsGranularity
import org.wordpress.android.fluxc.store.StatsStore.TimeStatsType.FILE_DOWNLOADS
import org.wordpress.android.fluxc.store.stats.time.FileDownloadsStore
import org.wordpress.android.modules.UI_THREAD
import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewFileDownloads
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.UseCaseMode.BLOCK
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.UseCaseMode.VIEW_ALL
import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem
import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Empty
import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Header
import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Link
import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ListItemWithIcon
import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.NavigationAction
import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.GranularStatelessUseCase
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.GranularUseCaseFactory
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.SelectedDateProvider
import org.wordpress.android.ui.stats.refresh.utils.ContentDescriptionHelper
import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider
import org.wordpress.android.ui.stats.refresh.utils.toFormattedString
import org.wordpress.android.ui.stats.refresh.utils.trackGranular
import org.wordpress.android.util.LocaleManagerWrapper
import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper
import java.util.Calendar
import java.util.Date
import javax.inject.Inject
import javax.inject.Named

private const val BLOCK_ITEM_COUNT = 6
private const val VIEW_ALL_ITEM_COUNT = 1000
private const val THRESHOLD_YEAR = 2019
private const val THRESHOLD_MONTH = Calendar.JUNE
private const val THRESHOLD_DAY = 29

class FileDownloadsUseCase
constructor(
statsGranularity: StatsGranularity,
@Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher,
private val store: FileDownloadsStore,
statsSiteProvider: StatsSiteProvider,
selectedDateProvider: SelectedDateProvider,
private val analyticsTracker: AnalyticsTrackerWrapper,
private val contentDescriptionHelper: ContentDescriptionHelper,
private val localeManagerWrapper: LocaleManagerWrapper,
private val useCaseMode: UseCaseMode
) : GranularStatelessUseCase<FileDownloadsModel>(
FILE_DOWNLOADS,
mainDispatcher,
selectedDateProvider,
statsSiteProvider,
statsGranularity
) {
private val itemsToLoad = if (useCaseMode == VIEW_ALL) VIEW_ALL_ITEM_COUNT else BLOCK_ITEM_COUNT

override fun buildLoadingItem(): List<BlockListItem> = listOf(Title(R.string.stats_file_downloads))

override suspend fun loadCachedData(selectedDate: Date, site: SiteModel): FileDownloadsModel? {
return store.getFileDownloads(
site,
statsGranularity,
LimitMode.Top(itemsToLoad),
selectedDate
)
}

override fun buildEmptyItem(): List<BlockListItem> {
val selectedDate = selectedDateProvider.getSelectedDate(statsGranularity)
val startCalendar = localeManagerWrapper.getCurrentCalendar()
startCalendar.set(THRESHOLD_YEAR, THRESHOLD_MONTH, THRESHOLD_DAY, 0, 0, 0)
return if (selectedDate != null && selectedDate.before(startCalendar.time)) {
buildLoadingItem() + listOf(Empty(textResource = R.string.stats_data_not_recorded_for_period))
} else {
super.buildEmptyItem()
}
}

override suspend fun fetchRemoteData(
selectedDate: Date,
site: SiteModel,
forced: Boolean
): State<FileDownloadsModel> {
val response = store.fetchFileDownloads(
site,
statsGranularity,
LimitMode.Top(itemsToLoad),
selectedDate,
forced
)
val model = response.model
val error = response.error

return when {
error != null -> State.Error(error.message ?: error.type.name)
model != null && model.fileDownloads.isNotEmpty() -> State.Data(model)
else -> State.Empty()
}
}

override fun buildUiModel(domainModel: FileDownloadsModel): List<BlockListItem> {
val items = mutableListOf<BlockListItem>()

if (useCaseMode == BLOCK) {
items.add(Title(R.string.stats_file_downloads))
}

if (domainModel.fileDownloads.isEmpty()) {
items.add(Empty(R.string.stats_no_data_for_period))
} else {
val header = Header(R.string.stats_file_downloads_title_label, R.string.stats_file_downloads_value_label)
items.add(header)
items.addAll(domainModel.fileDownloads.mapIndexed { index, fileDownloads ->
ListItemWithIcon(
text = fileDownloads.filename,
value = fileDownloads.downloads.toFormattedString(),
showDivider = index < domainModel.fileDownloads.size - 1,
contentDescription = contentDescriptionHelper.buildContentDescription(
header,
fileDownloads.filename,
fileDownloads.downloads
)
)
})

if (useCaseMode == BLOCK && domainModel.hasMore) {
items.add(
Link(
text = R.string.stats_insights_view_more,
navigateAction = NavigationAction.create(statsGranularity, this::onViewMoreClick)
)
)
}
}
return items
}

private fun onViewMoreClick(statsGranularity: StatsGranularity) {
analyticsTracker.trackGranular(AnalyticsTracker.Stat.STATS_FILE_DOWNLOADS_VIEW_MORE_TAPPED, statsGranularity)
navigateTo(
ViewFileDownloads(
statsGranularity,
selectedDateProvider.getSelectedDate(statsGranularity) ?: Date()
)
)
}

class FileDownloadsUseCaseFactory
@Inject constructor(
@Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher,
private val store: FileDownloadsStore,
private val selectedDateProvider: SelectedDateProvider,
private val statsSiteProvider: StatsSiteProvider,
private val analyticsTracker: AnalyticsTrackerWrapper,
private val contentDescriptionHelper: ContentDescriptionHelper,
private val localeManagerWrapper: LocaleManagerWrapper
) : GranularUseCaseFactory {
override fun build(granularity: StatsGranularity, useCaseMode: UseCaseMode) =
FileDownloadsUseCase(
granularity,
mainDispatcher,
store,
statsSiteProvider,
selectedDateProvider,
analyticsTracker,
contentDescriptionHelper,
localeManagerWrapper,
useCaseMode
)
}
}
@@ -13,6 +13,7 @@ import org.wordpress.android.ui.stats.StatsViewType.CLICKS
import org.wordpress.android.ui.stats.StatsViewType.COMMENTS
import org.wordpress.android.ui.stats.StatsViewType.DETAIL_MONTHS_AND_YEARS
import org.wordpress.android.ui.stats.StatsViewType.DETAIL_RECENT_WEEKS
import org.wordpress.android.ui.stats.StatsViewType.FILE_DOWNLOADS
import org.wordpress.android.ui.stats.StatsViewType.FOLLOWERS
import org.wordpress.android.ui.stats.StatsViewType.GEOVIEWS
import org.wordpress.android.ui.stats.StatsViewType.PUBLICIZE
@@ -29,6 +30,7 @@ import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewAuthors
import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewClicks
import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewCommentsStats
import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewCountries
import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewFileDownloads
import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewFollowersStats
import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewInsightsManagement
import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewMonthsAndYearsStats
@@ -171,6 +173,14 @@ class StatsNavigator
siteProvider.siteModel.id
)
}
is ViewFileDownloads -> {
ActivityLauncher.viewAllGranularStats(
activity,
target.statsGranularity,
FILE_DOWNLOADS,
siteProvider.siteModel.id
)
}
is ViewAnnualStats -> {
ActivityLauncher.viewAllGranularStats(activity, YEARS, ANNUAL_STATS, siteProvider.siteModel.id)
}
@@ -830,6 +830,7 @@
<string name="stats_menu_move_up">Move up</string>
<string name="stats_menu_move_down">Move down</string>
<string name="stats_menu_remove">Remove from insights</string>
<string name="stats_data_not_recorded_for_period">File download stats were not recorded before June 28th 2019.</string>

<!-- Stats accessibility strings -->
<string name="stats_loading_card">Loading selected card data</string>
@@ -1023,6 +1024,9 @@
<string name="stats_author_views_label">Views</string>
<string name="stats_post_label">Post</string>
<string name="stats_post_views_label">Views</string>
<string name="stats_file_downloads">File downloads</string>
<string name="stats_file_downloads_title_label">File</string>
<string name="stats_file_downloads_value_label">Downloads</string>

<string name="stats_select_previous_period_description">Select previous period</string>
<string name="stats_select_next_period_description">Select next period</string>
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.