Skip to content

Commit ebbf23e

Browse files
committed
Move helper for sorting order stats intervals to helper class
1 parent eda9e85 commit ebbf23e

File tree

3 files changed

+55
-59
lines changed

3 files changed

+55
-59
lines changed

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

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,16 @@ import WooFoundation
55
/// Helpers for calculating and displaying Stats V4 data
66
///
77
final class StatsV4DataHelper {
8-
typealias OrderStatsData = (stats: OrderStatsV4?, intervals: [OrderStatsV4Interval])
98

109
// MARK: Revenue Stats
1110

1211
/// Creates the text to display for the total revenue.
1312
///
14-
static func createTotalRevenueText(orderStatsData: OrderStatsData,
13+
static func createTotalRevenueText(orderStats: OrderStatsV4?,
1514
selectedIntervalIndex: Int?,
1615
currencyFormatter: CurrencyFormatter?,
1716
currencyCode: String) -> String {
18-
if let revenue = totalRevenue(at: selectedIntervalIndex, orderStats: orderStatsData.stats, orderStatsIntervals: orderStatsData.intervals) {
17+
if let revenue = totalRevenue(at: selectedIntervalIndex, orderStats: orderStats) {
1918
// If revenue is an integer, no decimal points are shown.
2019
let numberOfDecimals: Int? = revenue.isInteger ? 0: nil
2120
let currencyFormatter = currencyFormatter ?? CurrencyFormatter(currencySettings: ServiceLocator.currencySettings)
@@ -29,8 +28,8 @@ final class StatsV4DataHelper {
2928

3029
/// Creates the text to display for the order count.
3130
///
32-
static func createOrderCountText(orderStatsData: OrderStatsData, selectedIntervalIndex: Int?) -> String {
33-
if let count = orderCount(at: selectedIntervalIndex, orderStats: orderStatsData.stats, orderStatsIntervals: orderStatsData.intervals) {
31+
static func createOrderCountText(orderStats: OrderStatsV4?, selectedIntervalIndex: Int?) -> String {
32+
if let count = orderCount(at: selectedIntervalIndex, orderStats: orderStats) {
3433
return Double(count).humanReadableString()
3534
} else {
3635
return Constants.placeholderText
@@ -39,8 +38,8 @@ final class StatsV4DataHelper {
3938

4039
/// Creates the text to display for the average order value.
4140
///
42-
static func createAverageOrderValueText(orderStatsData: OrderStatsData, currencyFormatter: CurrencyFormatter, currencyCode: String) -> String {
43-
if let value = averageOrderValue(orderStats: orderStatsData.stats) {
41+
static func createAverageOrderValueText(orderStats: OrderStatsV4?, currencyFormatter: CurrencyFormatter, currencyCode: String) -> String {
42+
if let value = averageOrderValue(orderStats: orderStats) {
4443
// If order value is an integer, no decimal points are shown.
4544
let numberOfDecimals: Int? = value.isInteger ? 0 : nil
4645
return currencyFormatter.formatAmount(value, with: currencyCode, numberOfDecimals: numberOfDecimals) ?? String()
@@ -65,9 +64,9 @@ final class StatsV4DataHelper {
6564

6665
/// Creates the text to display for the conversion rate.
6766
///
68-
static func createConversionRateText(orderStatsData: OrderStatsData, siteStats: SiteVisitStats?, selectedIntervalIndex: Int?) -> String {
67+
static func createConversionRateText(orderStats: OrderStatsV4?, siteStats: SiteVisitStats?, selectedIntervalIndex: Int?) -> String {
6968
let visitors = visitorCount(at: selectedIntervalIndex, siteStats: siteStats)
70-
let orders = orderCount(at: selectedIntervalIndex, orderStats: orderStatsData.stats, orderStatsIntervals: orderStatsData.intervals)
69+
let orders = orderCount(at: selectedIntervalIndex, orderStats: orderStats)
7170

7271
let numberFormatter = NumberFormatter()
7372
numberFormatter.numberStyle = .percent
@@ -83,6 +82,17 @@ final class StatsV4DataHelper {
8382
return Constants.placeholderText
8483
}
8584
}
85+
86+
// MARK: Stats Intervals
87+
88+
/// Returns the order stats intervals, ordered by date.
89+
///
90+
static func sortOrderStatsIntervals(from orderStats: OrderStatsV4?) -> [OrderStatsV4Interval] {
91+
return orderStats?.intervals.sorted(by: { (lhs, rhs) -> Bool in
92+
let siteTimezone = TimeZone.siteTimezone
93+
return lhs.dateStart(timeZone: siteTimezone) < rhs.dateStart(timeZone: siteTimezone)
94+
}) ?? []
95+
}
8696
}
8797

8898
// MARK: - Private helpers
@@ -105,7 +115,8 @@ private extension StatsV4DataHelper {
105115

106116
/// Retrieves the order count for the provided order stats and, optionally, a specific interval.
107117
///
108-
static func orderCount(at selectedIndex: Int?, orderStats: OrderStatsV4?, orderStatsIntervals: [OrderStatsV4Interval]) -> Double? {
118+
static func orderCount(at selectedIndex: Int?, orderStats: OrderStatsV4?) -> Double? {
119+
let orderStatsIntervals = sortOrderStatsIntervals(from: orderStats)
109120
if let selectedIndex, selectedIndex < orderStatsIntervals.count {
110121
let orderStats = orderStatsIntervals[selectedIndex]
111122
return Double(orderStats.subtotals.totalOrders)
@@ -128,7 +139,8 @@ private extension StatsV4DataHelper {
128139

129140
/// Retrieves the total revenue from the provided order stats and, optionally, a specific interval.
130141
///
131-
static func totalRevenue(at selectedIndex: Int?, orderStats: OrderStatsV4?, orderStatsIntervals: [OrderStatsV4Interval]) -> Decimal? {
142+
static func totalRevenue(at selectedIndex: Int?, orderStats: OrderStatsV4?) -> Decimal? {
143+
let orderStatsIntervals = sortOrderStatsIntervals(from: orderStats)
132144
if let selectedIndex, selectedIndex < orderStatsIntervals.count {
133145
let orderStats = orderStatsIntervals[selectedIndex]
134146
return orderStats.subtotals.grossRevenue

WooCommerce/Classes/ViewRelated/Dashboard/Stats v4/StoreStatsPeriodViewModel.swift

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ final class StoreStatsPeriodViewModel {
2222
private(set) lazy var orderStatsText: AnyPublisher<String, Never> =
2323
Publishers.CombineLatest($orderStatsData.eraseToAnyPublisher(), $selectedIntervalIndex.eraseToAnyPublisher())
2424
.compactMap { [weak self] orderStatsData, selectedIntervalIndex in
25-
StatsV4DataHelper.createOrderCountText(orderStatsData: orderStatsData, selectedIntervalIndex: selectedIntervalIndex)
25+
StatsV4DataHelper.createOrderCountText(orderStats: orderStatsData.stats, selectedIntervalIndex: selectedIntervalIndex)
2626
}
2727
.removeDuplicates()
2828
.eraseToAnyPublisher()
2929

3030
/// Emits revenue stats text values based on order stats, selected time interval, and currency code.
3131
private(set) lazy var revenueStatsText: AnyPublisher<String, Never> = $orderStatsData.combineLatest($selectedIntervalIndex, currencySettings.$currencyCode)
3232
.compactMap { [weak self] orderStatsData, selectedIntervalIndex, currencyCode in
33-
StatsV4DataHelper.createTotalRevenueText(orderStatsData: orderStatsData,
33+
StatsV4DataHelper.createTotalRevenueText(orderStats: orderStatsData.stats,
3434
selectedIntervalIndex: selectedIntervalIndex,
3535
currencyFormatter: self?.currencyFormatter,
3636
currencyCode: currencyCode.rawValue)
@@ -51,7 +51,7 @@ final class StoreStatsPeriodViewModel {
5151
private(set) lazy var conversionStatsText: AnyPublisher<String, Never> =
5252
Publishers.CombineLatest3($orderStatsData.eraseToAnyPublisher(), $siteStats.eraseToAnyPublisher(), $selectedIntervalIndex.eraseToAnyPublisher())
5353
.compactMap { orderStatsData, siteStats, selectedIntervalIndex in
54-
StatsV4DataHelper.createConversionRateText(orderStatsData: orderStatsData, siteStats: siteStats, selectedIntervalIndex: selectedIntervalIndex)
54+
StatsV4DataHelper.createConversionRateText(orderStats: orderStatsData.stats, siteStats: siteStats, selectedIntervalIndex: selectedIntervalIndex)
5555
}
5656
.removeDuplicates()
5757
.eraseToAnyPublisher()
@@ -230,16 +230,6 @@ private extension StoreStatsPeriodViewModel {
230230
}
231231
}
232232

233-
// MARK: - Private data helpers
234-
//
235-
private extension StoreStatsPeriodViewModel {
236-
func orderStatsIntervals(from orderStats: OrderStatsV4?) -> [OrderStatsV4Interval] {
237-
return orderStats?.intervals.sorted(by: { (lhs, rhs) -> Bool in
238-
return lhs.dateStart(timeZone: siteTimezone) < rhs.dateStart(timeZone: siteTimezone)
239-
}) ?? []
240-
}
241-
}
242-
243233
// MARK: - Results controller
244234
//
245235
private extension StoreStatsPeriodViewModel {
@@ -278,7 +268,7 @@ private extension StoreStatsPeriodViewModel {
278268

279269
func updateOrderDataIfNeeded() {
280270
let orderStats = orderStatsResultsController.fetchedObjects.first
281-
let intervals = orderStatsIntervals(from: orderStats)
271+
let intervals = StatsV4DataHelper.sortOrderStatsIntervals(from: orderStats)
282272
orderStatsData = (stats: orderStats, intervals: intervals)
283273
}
284274
}

WooCommerce/WooCommerceTests/ViewRelated/Dashboard/StatsV4DataHelperTests.swift

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ final class StatsV4DataHelperTests: XCTestCase {
1717
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(grossRevenue: 62))
1818

1919
// When
20-
let totalRevenue = StatsV4DataHelper.createTotalRevenueText(orderStatsData: (orderStats, []),
20+
let totalRevenue = StatsV4DataHelper.createTotalRevenueText(orderStats: orderStats,
2121
selectedIntervalIndex: nil,
2222
currencyFormatter: currencyFormatter,
2323
currencyCode: currencyCode.rawValue)
@@ -31,7 +31,7 @@ final class StatsV4DataHelperTests: XCTestCase {
3131
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(grossRevenue: 62.856))
3232

3333
// When
34-
let totalRevenue = StatsV4DataHelper.createTotalRevenueText(orderStatsData: (orderStats, []),
34+
let totalRevenue = StatsV4DataHelper.createTotalRevenueText(orderStats: orderStats,
3535
selectedIntervalIndex: nil,
3636
currencyFormatter: currencyFormatter,
3737
currencyCode: currencyCode.rawValue)
@@ -43,17 +43,18 @@ final class StatsV4DataHelperTests: XCTestCase {
4343
func test_createTotalRevenueText_returns_expected_text_for_selected_interval() {
4444
// Given
4545
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(grossRevenue: 62.7),
46-
intervals: [.fake().copy(dateStart: "2019-07-09 01:00:00",
47-
dateEnd: "2019-07-09 01:59:59",
48-
subtotals: .fake().copy(grossRevenue: 25)),
49-
.fake().copy(dateStart: "2019-07-09 00:00:00",
50-
dateEnd: "2019-07-09 00:59:59",
51-
subtotals: .fake().copy(grossRevenue: 31))
52-
])
46+
intervals: [.fake().copy(dateStart: "2019-07-09 01:00:00",
47+
dateEnd: "2019-07-09 01:59:59",
48+
subtotals: .fake().copy(grossRevenue: 25)),
49+
.fake().copy(dateStart: "2019-07-09 00:00:00",
50+
dateEnd: "2019-07-09 00:59:59",
51+
subtotals: .fake().copy(grossRevenue: 31))
52+
])
53+
let selectedIntervalIndex = 1 // Corresponds to the second earliest interval, which is the first interval in `OrderStatsV4`.
5354

5455
// When
55-
let totalRevenue = StatsV4DataHelper.createTotalRevenueText(orderStatsData: (orderStats, orderStats.intervals),
56-
selectedIntervalIndex: 0,
56+
let totalRevenue = StatsV4DataHelper.createTotalRevenueText(orderStats: orderStats,
57+
selectedIntervalIndex: selectedIntervalIndex,
5758
currencyFormatter: currencyFormatter,
5859
currencyCode: currencyCode.rawValue)
5960

@@ -68,7 +69,7 @@ final class StatsV4DataHelperTests: XCTestCase {
6869
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(totalOrders: 3))
6970

7071
// When
71-
let orderCount = StatsV4DataHelper.createOrderCountText(orderStatsData: (orderStats, []), selectedIntervalIndex: 0)
72+
let orderCount = StatsV4DataHelper.createOrderCountText(orderStats: orderStats, selectedIntervalIndex: nil)
7273

7374
// Then
7475
XCTAssertEqual(orderCount, "3")
@@ -77,16 +78,17 @@ final class StatsV4DataHelperTests: XCTestCase {
7778
func test_createOrderCountText_returns_expected_text_for_selected_interval() {
7879
// Given
7980
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(totalOrders: 3),
80-
intervals: [.fake().copy(dateStart: "2019-07-09 01:00:00",
81-
dateEnd: "2019-07-09 01:59:59",
82-
subtotals: .fake().copy(totalOrders: 1, grossRevenue: 25)),
83-
.fake().copy(dateStart: "2019-07-09 00:00:00",
84-
dateEnd: "2019-07-09 00:59:59",
85-
subtotals: .fake().copy(totalOrders: 2, grossRevenue: 31))
86-
])
81+
intervals: [.fake().copy(dateStart: "2019-07-09 01:00:00",
82+
dateEnd: "2019-07-09 01:59:59",
83+
subtotals: .fake().copy(totalOrders: 1, grossRevenue: 25)),
84+
.fake().copy(dateStart: "2019-07-09 00:00:00",
85+
dateEnd: "2019-07-09 00:59:59",
86+
subtotals: .fake().copy(totalOrders: 2, grossRevenue: 31))
87+
])
88+
let selectedIntervalIndex = 1 // Corresponds to the second earliest interval, which is the first interval in `OrderStatsV4`.
8789

8890
// When
89-
let orderCount = StatsV4DataHelper.createOrderCountText(orderStatsData: (orderStats, orderStats.intervals), selectedIntervalIndex: 0)
91+
let orderCount = StatsV4DataHelper.createOrderCountText(orderStats: orderStats, selectedIntervalIndex: selectedIntervalIndex)
9092

9193
// Then
9294
XCTAssertEqual(orderCount, "1")
@@ -97,7 +99,7 @@ final class StatsV4DataHelperTests: XCTestCase {
9799
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(averageOrderValue: 62))
98100

99101
// When
100-
let averageOrderValue = StatsV4DataHelper.createAverageOrderValueText(orderStatsData: (orderStats, []),
102+
let averageOrderValue = StatsV4DataHelper.createAverageOrderValueText(orderStats: orderStats,
101103
currencyFormatter: currencyFormatter,
102104
currencyCode: currencyCode.rawValue)
103105

@@ -110,7 +112,7 @@ final class StatsV4DataHelperTests: XCTestCase {
110112
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(averageOrderValue: 62.856))
111113

112114
// When
113-
let averageOrderValue = StatsV4DataHelper.createAverageOrderValueText(orderStatsData: (orderStats, []),
115+
let averageOrderValue = StatsV4DataHelper.createAverageOrderValueText(orderStats: orderStats,
114116
currencyFormatter: currencyFormatter,
115117
currencyCode: currencyCode.rawValue)
116118

@@ -156,9 +158,7 @@ final class StatsV4DataHelperTests: XCTestCase {
156158
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(totalOrders: 3))
157159

158160
// When
159-
let conversionRate = StatsV4DataHelper.createConversionRateText(orderStatsData: (orderStats, []),
160-
siteStats: siteVisitStats,
161-
selectedIntervalIndex: nil)
161+
let conversionRate = StatsV4DataHelper.createConversionRateText(orderStats: orderStats, siteStats: siteVisitStats, selectedIntervalIndex: nil)
162162

163163
// Then
164164
XCTAssertEqual(conversionRate, "0%")
@@ -170,9 +170,7 @@ final class StatsV4DataHelperTests: XCTestCase {
170170
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(totalOrders: 3557))
171171

172172
// When
173-
let conversionRate = StatsV4DataHelper.createConversionRateText(orderStatsData: (orderStats, []),
174-
siteStats: siteVisitStats,
175-
selectedIntervalIndex: nil)
173+
let conversionRate = StatsV4DataHelper.createConversionRateText(orderStats: orderStats, siteStats: siteVisitStats, selectedIntervalIndex: nil)
176174

177175
// Then
178176
XCTAssertEqual(conversionRate, "35.6%") // order count: 3557, visitor count: 10000 => 0.3557 (35.57%)
@@ -184,9 +182,7 @@ final class StatsV4DataHelperTests: XCTestCase {
184182
let orderStats = OrderStatsV4.fake().copy(totals: .fake().copy(totalOrders: 3))
185183

186184
// When
187-
let conversionRate = StatsV4DataHelper.createConversionRateText(orderStatsData: (orderStats, []),
188-
siteStats: siteVisitStats,
189-
selectedIntervalIndex: nil)
185+
let conversionRate = StatsV4DataHelper.createConversionRateText(orderStats: orderStats, siteStats: siteVisitStats, selectedIntervalIndex: nil)
190186

191187
// Then
192188
XCTAssertEqual(conversionRate, "30%") // order count: 3, visitor count: 10 => 0.3 (30%)
@@ -199,9 +195,7 @@ final class StatsV4DataHelperTests: XCTestCase {
199195
intervals: [.fake().copy(subtotals: .fake().copy(totalOrders: 1))])
200196

201197
// When
202-
let conversionRate = StatsV4DataHelper.createConversionRateText(orderStatsData: (orderStats, orderStats.intervals),
203-
siteStats: siteVisitStats,
204-
selectedIntervalIndex: 0)
198+
let conversionRate = StatsV4DataHelper.createConversionRateText(orderStats: orderStats, siteStats: siteVisitStats, selectedIntervalIndex: 0)
205199

206200
// Then
207201
XCTAssertEqual(conversionRate, "10%")

0 commit comments

Comments
 (0)