Skip to content

Commit cc593fb

Browse files
authored
Merge pull request #8395 from woocommerce/issue/8364-data-formatting
[Analytics Hub] Sessions card data formatting
2 parents cddcea4 + 7f6731e commit cc593fb

File tree

6 files changed

+126
-19
lines changed

6 files changed

+126
-19
lines changed

Fakes/Fakes/Networking.generated.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,6 +1593,19 @@ extension SiteSettingGroup {
15931593
.general
15941594
}
15951595
}
1596+
extension Networking.SiteSummaryStats {
1597+
/// Returns a "ready to use" type filled with fake values.
1598+
///
1599+
public static func fake() -> Networking.SiteSummaryStats {
1600+
.init(
1601+
siteID: .fake(),
1602+
date: .fake(),
1603+
period: .fake(),
1604+
visitors: .fake(),
1605+
views: .fake()
1606+
)
1607+
}
1608+
}
15961609
extension Networking.SiteVisitStats {
15971610
/// Returns a "ready to use" type filled with fake values.
15981611
///

Networking/Networking/Model/Copiable/Models+Copiable.generated.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1912,6 +1912,30 @@ extension Networking.SiteSetting {
19121912
}
19131913
}
19141914

1915+
extension Networking.SiteSummaryStats {
1916+
public func copy(
1917+
siteID: CopiableProp<Int64> = .copy,
1918+
date: CopiableProp<String> = .copy,
1919+
period: CopiableProp<StatGranularity> = .copy,
1920+
visitors: CopiableProp<Int> = .copy,
1921+
views: CopiableProp<Int> = .copy
1922+
) -> Networking.SiteSummaryStats {
1923+
let siteID = siteID ?? self.siteID
1924+
let date = date ?? self.date
1925+
let period = period ?? self.period
1926+
let visitors = visitors ?? self.visitors
1927+
let views = views ?? self.views
1928+
1929+
return Networking.SiteSummaryStats(
1930+
siteID: siteID,
1931+
date: date,
1932+
period: period,
1933+
visitors: visitors,
1934+
views: views
1935+
)
1936+
}
1937+
}
1938+
19151939
extension Networking.SiteVisitStats {
19161940
public func copy(
19171941
siteID: CopiableProp<Int64> = .copy,

Networking/Networking/Model/Stats/SiteSummaryStats.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Codegen
33

44
/// Represents site summary stats for a specific period.
55
///
6-
public struct SiteSummaryStats: Decodable {
6+
public struct SiteSummaryStats: Decodable, GeneratedCopiable, GeneratedFakeable {
77
public let siteID: Int64
88
public let date: String
99
public let period: StatGranularity

WooCommerce/Classes/ViewRelated/Dashboard/Factories/StatsDataTextFormatter.swift

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -118,27 +118,37 @@ struct StatsDataTextFormatter {
118118
return createDeltaPercentage(from: previousCount, to: currentCount)
119119
}
120120

121+
/// Creates the text to display for the views count.
122+
///
123+
static func createViewsCountText(siteStats: SiteSummaryStats?) -> String {
124+
guard let viewsCount = siteStats?.views else {
125+
return Constants.placeholderText
126+
}
127+
128+
return Double(viewsCount).humanReadableString()
129+
}
130+
121131
// MARK: Conversion Stats
122132

123133
/// Creates the text to display for the conversion rate.
124134
///
125135
static func createConversionRateText(orderStats: OrderStatsV4?, siteStats: SiteVisitStats?, selectedIntervalIndex: Int?) -> String {
126-
let visitors = visitorCount(at: selectedIntervalIndex, siteStats: siteStats)
127-
let orders = orderCount(at: selectedIntervalIndex, orderStats: orderStats)
136+
guard let visitors = visitorCount(at: selectedIntervalIndex, siteStats: siteStats),
137+
let orders = orderCount(at: selectedIntervalIndex, orderStats: orderStats) else {
138+
return Constants.placeholderText
139+
}
128140

129-
let numberFormatter = NumberFormatter()
130-
numberFormatter.numberStyle = .percent
131-
numberFormatter.minimumFractionDigits = 1
141+
return createConversionRateText(converted: orders, total: visitors)
142+
}
132143

133-
if let visitors, let orders {
134-
// Maximum conversion rate is 100%.
135-
let conversionRate = visitors > 0 ? min(orders/visitors, 1): 0
136-
let minimumFractionDigits = floor(conversionRate * 100.0) == conversionRate * 100.0 ? 0: 1
137-
numberFormatter.minimumFractionDigits = minimumFractionDigits
138-
return numberFormatter.string(from: conversionRate as NSNumber) ?? Constants.placeholderText
139-
} else {
144+
/// Creates the text to display for the conversion rate based on SiteSummaryStats data.
145+
///
146+
static func createConversionRateText(orderStats: OrderStatsV4?, siteStats: SiteSummaryStats?) -> String {
147+
guard let visitors = siteStats?.visitors, let orders = orderStats?.totals.totalOrders else {
140148
return Constants.placeholderText
141149
}
150+
151+
return createConversionRateText(converted: Double(orders), total: Double(visitors))
142152
}
143153

144154
// MARK: Product Stats
@@ -261,9 +271,21 @@ private extension StatsDataTextFormatter {
261271
}
262272
}
263273

274+
/// Creates the text to display for the conversion rate from 2 input values.
275+
///
276+
static func createConversionRateText(converted: Double, total: Double) -> String {
277+
let numberFormatter = NumberFormatter()
278+
numberFormatter.numberStyle = .percent
279+
numberFormatter.minimumFractionDigits = 1
280+
281+
// Maximum conversion rate is 100%.
282+
let conversionRate = total > 0 ? min(converted/total, 1) : 0
283+
let minimumFractionDigits = floor(conversionRate * 100.0) == conversionRate * 100.0 ? 0 : 1
284+
numberFormatter.minimumFractionDigits = minimumFractionDigits
285+
return numberFormatter.string(from: conversionRate as NSNumber) ?? Constants.placeholderText
286+
}
287+
264288
enum Constants {
265289
static let placeholderText = "-"
266290
}
267-
268-
269291
}

WooCommerce/WooCommerceTests/ViewRelated/Dashboard/Factories/StatsDataTextFormatterTests.swift

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,20 @@ final class StatsDataTextFormatterTests: XCTestCase {
333333
XCTAssertEqual(visitorCountDelta.direction, .positive)
334334
}
335335

336+
func test_createViewsCountText_returns_expected_views_stats() {
337+
// Given
338+
let siteVisitStats = SiteSummaryStats.fake().copy(views: 250)
339+
340+
// When
341+
let viewsCount = StatsDataTextFormatter.createViewsCountText(siteStats: siteVisitStats)
342+
343+
// Then
344+
XCTAssertEqual(viewsCount, "250")
345+
}
346+
336347
// MARK: Conversion Stats
337348

338-
func test_createConversionRateText_returns_placeholder_when_visitor_count_is_zero() {
349+
func test_createConversionRateText_for_SiteVisitStats_returns_placeholder_when_visitor_count_is_zero() {
339350
// Given
340351
let siteVisitStats = Yosemite.SiteVisitStats.fake().copy(items: [.fake().copy(visitors: 0)])
341352
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(totalOrders: 3))
@@ -347,7 +358,7 @@ final class StatsDataTextFormatterTests: XCTestCase {
347358
XCTAssertEqual(conversionRate, "0%")
348359
}
349360

350-
func test_createConversionRateText_returns_one_decimal_point_when_percentage_value_has_two_decimal_points() {
361+
func test_createConversionRateText_for_SiteVisitStats_returns_one_decimal_point_when_percentage_value_has_two_decimal_points() {
351362
// Given
352363
let siteVisitStats = Yosemite.SiteVisitStats.fake().copy(items: [.fake().copy(visitors: 10000)])
353364
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(totalOrders: 3557))
@@ -359,7 +370,7 @@ final class StatsDataTextFormatterTests: XCTestCase {
359370
XCTAssertEqual(conversionRate, "35.6%") // order count: 3557, visitor count: 10000 => 0.3557 (35.57%)
360371
}
361372

362-
func test_createConversionRateText_returns_no_decimal_point_when_percentage_value_is_integer() {
373+
func test_createConversionRateText_for_SiteVisitStats_returns_no_decimal_point_when_percentage_value_is_integer() {
363374
// Given
364375
let siteVisitStats = Yosemite.SiteVisitStats.fake().copy(items: [.fake().copy(visitors: 10)])
365376
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(totalOrders: 3))
@@ -371,7 +382,7 @@ final class StatsDataTextFormatterTests: XCTestCase {
371382
XCTAssertEqual(conversionRate, "30%") // order count: 3, visitor count: 10 => 0.3 (30%)
372383
}
373384

374-
func test_createConversionRateText_returns_expected_text_for_selected_interval() {
385+
func test_createConversionRateText_for_SiteVisitStats_returns_expected_text_for_selected_interval() {
375386
// Given
376387
let siteVisitStats = Yosemite.SiteVisitStats.fake().copy(items: [.fake().copy(visitors: 10)])
377388
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(totalOrders: 2),
@@ -384,6 +395,42 @@ final class StatsDataTextFormatterTests: XCTestCase {
384395
XCTAssertEqual(conversionRate, "10%")
385396
}
386397

398+
func test_createConversionRateText_for_SiteSummaryStats_returns_placeholder_when_visitor_count_is_zero() {
399+
// Given
400+
let siteSummaryStats = SiteSummaryStats.fake().copy(visitors: 0)
401+
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(totalOrders: 3))
402+
403+
// When
404+
let conversionRate = StatsDataTextFormatter.createConversionRateText(orderStats: orderStats, siteStats: siteSummaryStats)
405+
406+
// Then
407+
XCTAssertEqual(conversionRate, "0%")
408+
}
409+
410+
func test_createConversionRateText_for_SiteSummaryStats_returns_one_decimal_point_when_percentage_value_has_two_decimal_points() {
411+
// Given
412+
let siteSummaryStats = SiteSummaryStats.fake().copy(visitors: 10000)
413+
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(totalOrders: 3557))
414+
415+
// When
416+
let conversionRate = StatsDataTextFormatter.createConversionRateText(orderStats: orderStats, siteStats: siteSummaryStats)
417+
418+
// Then
419+
XCTAssertEqual(conversionRate, "35.6%") // order count: 3557, visitor count: 10000 => 0.3557 (35.57%)
420+
}
421+
422+
func test_createConversionRateText_for_SiteSummaryStats_returns_no_decimal_point_when_percentage_value_is_integer() {
423+
// Given
424+
let siteSummaryStats = SiteSummaryStats.fake().copy(visitors: 10)
425+
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(totalOrders: 3))
426+
427+
// When
428+
let conversionRate = StatsDataTextFormatter.createConversionRateText(orderStats: orderStats, siteStats: siteSummaryStats)
429+
430+
// Then
431+
XCTAssertEqual(conversionRate, "30%") // order count: 3, visitor count: 10 => 0.3 (30%)
432+
}
433+
387434
// MARK: Delta Calculations
388435

389436
func test_createDeltaPercentage_returns_expected_positive_delta() {

Yosemite/Yosemite/Model/Model.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ public typealias SitePlugin = Networking.SitePlugin
125125
public typealias SitePluginStatusEnum = Networking.SitePluginStatusEnum
126126
public typealias SiteSetting = Networking.SiteSetting
127127
public typealias SiteSettingGroup = Networking.SiteSettingGroup
128+
public typealias SiteSummaryStats = Networking.SiteSummaryStats
128129
public typealias SiteVisitStats = Networking.SiteVisitStats
129130
public typealias SiteVisitStatsItem = Networking.SiteVisitStatsItem
130131
public typealias StateOfACountry = Networking.StateOfACountry

0 commit comments

Comments
 (0)