Skip to content
This repository was archived by the owner on Sep 15, 2025. It is now read-only.

Commit a8b31bc

Browse files
committed
Add initial StatsArchiveTimeIntervalData
1 parent 392d2d3 commit a8b31bc

File tree

4 files changed

+217
-0
lines changed

4 files changed

+217
-0
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import Foundation
2+
3+
public struct StatsArchiveTimeIntervalData {
4+
public let period: StatsPeriodUnit
5+
public let unit: StatsPeriodUnit?
6+
public let periodEndDate: Date
7+
public let summary: StatsArchiveSummary
8+
9+
public init(period: StatsPeriodUnit,
10+
unit: StatsPeriodUnit? = nil,
11+
periodEndDate: Date,
12+
summary: StatsArchiveSummary) {
13+
self.period = period
14+
self.unit = unit
15+
self.periodEndDate = periodEndDate
16+
self.summary = summary
17+
}
18+
}
19+
20+
public struct StatsArchiveSummary {
21+
public let other: [StatsArchiveItem]
22+
public let author: [StatsArchiveItem]
23+
24+
public init(other: [StatsArchiveItem], author: [StatsArchiveItem]) {
25+
self.other = other
26+
self.author = author
27+
}
28+
}
29+
30+
public struct StatsArchiveItem {
31+
public let href: String
32+
public let value: String
33+
public let views: Int
34+
35+
public init(href: String, value: String, views: Int) {
36+
self.href = href
37+
self.value = value
38+
self.views = views
39+
}
40+
}
41+
42+
extension StatsArchiveTimeIntervalData: StatsTimeIntervalData {
43+
public static var pathComponent: String {
44+
return "stats/archives"
45+
}
46+
47+
public static func queryProperties(with date: Date, period: StatsPeriodUnit, maxCount: Int) -> [String: String] {
48+
return ["max": String(maxCount)]
49+
}
50+
51+
public init?(date: Date, period: StatsPeriodUnit, jsonDictionary: [String: AnyObject]) {
52+
self.init(date: date, period: period, unit: nil, jsonDictionary: jsonDictionary)
53+
}
54+
55+
public init?(date: Date, period: StatsPeriodUnit, unit: StatsPeriodUnit?, jsonDictionary: [String: AnyObject]) {
56+
guard let summary = jsonDictionary["summary"] as? [String: AnyObject] else {
57+
return nil
58+
}
59+
60+
let otherItems = (summary["other"] as? [[String: AnyObject]] ?? []).compactMap { StatsArchiveItem(jsonDictionary: $0) }
61+
let authorItems = (summary["author"] as? [[String: AnyObject]] ?? []).compactMap { StatsArchiveItem(jsonDictionary: $0) }
62+
63+
let archiveSummary = StatsArchiveSummary(other: otherItems, author: authorItems)
64+
65+
self.period = period
66+
self.unit = unit
67+
self.periodEndDate = date
68+
self.summary = archiveSummary
69+
}
70+
}
71+
72+
private extension StatsArchiveItem {
73+
init?(jsonDictionary: [String: AnyObject]) {
74+
guard
75+
let href = jsonDictionary["href"] as? String,
76+
let value = jsonDictionary["value"] as? String,
77+
let views = jsonDictionary["views"] as? Int
78+
else {
79+
return nil
80+
}
81+
82+
self.href = href
83+
self.value = value
84+
self.views = views
85+
}
86+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{
2+
"date": "2025-07-21",
3+
"period": "day",
4+
"summary": {
5+
"other": [
6+
{
7+
"href": "http://example.com/wp-admin/admin.php?page=stats",
8+
"value": "/wp-admin/admin.php?page=stats",
9+
"views": 10
10+
},
11+
{
12+
"href": "http://example.com/wp-admin/",
13+
"value": "/wp-admin/",
14+
"views": 4
15+
},
16+
{
17+
"href": "http://example.com/wp-admin/edit.php",
18+
"value": "/wp-admin/edit.php",
19+
"views": 4
20+
},
21+
{
22+
"href": "http://example.com/wp-admin/index.php",
23+
"value": "/wp-admin/index.php",
24+
"views": 2
25+
},
26+
{
27+
"href": "http://example.com/wp-admin/revision.php?revision=12345",
28+
"value": "/wp-admin/revision.php?revision=12345",
29+
"views": 2
30+
},
31+
{
32+
"href": "http://example.com/wp-admin/admin.php?page=settings",
33+
"value": "/wp-admin/admin.php?page=settings",
34+
"views": 1
35+
},
36+
{
37+
"href": "http://example.com/wp-admin/post.php?post=67890&action=edit",
38+
"value": "/wp-admin/post.php?post=67890&action=edit",
39+
"views": 1
40+
},
41+
{
42+
"href": "http://example.com/wp-admin/profile.php",
43+
"value": "/wp-admin/profile.php",
44+
"views": 1
45+
}
46+
],
47+
"author": [
48+
{
49+
"href": "http://example.com/author/johndoe/",
50+
"value": "johndoe",
51+
"views": 31
52+
},
53+
{
54+
"href": "http://example.com/author/janedoe/",
55+
"value": "janedoe",
56+
"views": 5
57+
},
58+
{
59+
"href": "http://example.com/author/testuser/",
60+
"value": "testuser",
61+
"views": 2
62+
},
63+
{
64+
"href": "http://example.com/author//",
65+
"value": "",
66+
"views": 2
67+
}
68+
]
69+
}
70+
}

Tests/WordPressKitTests/Tests/StatsRemoteV2Tests.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class StatsRemoteV2Tests: RemoteTestCase, RESTTestable {
2626
let getPostsDetailsFilename = "stats-post-details.json"
2727
let toggleSpamStateResponseFilename = "stats-referrer-mark-as-spam.json"
2828
let getStatsSummaryFilename = "stats-summary.json"
29+
let getArchivesDataFilename = "stats-archives-data.json"
2930

3031
// MARK: - Properties
3132

@@ -42,6 +43,7 @@ class StatsRemoteV2Tests: RemoteTestCase, RESTTestable {
4243
var siteDownloadsDataEndpoint: String { return "sites/\(siteID)/stats/file-downloads/" }
4344
var sitePostDetailsEndpoint: String { return "sites/\(siteID)/stats/post/9001" }
4445
var siteStatsSummaryEndpoint: String { return "sites/\(siteID)/stats/summary/" }
46+
var siteArchivesDataEndpoint: String { return "sites/\(siteID)/stats/archives" }
4547

4648
func toggleSpamStateEndpoint(for referrerDomain: String, markAsSpam: Bool) -> String {
4749
let action = markAsSpam ? "new" : "delete"
@@ -738,4 +740,55 @@ class StatsRemoteV2Tests: RemoteTestCase, RESTTestable {
738740
waitForExpectations(timeout: timeout, handler: nil)
739741

740742
}
743+
744+
func testArchives() {
745+
let expect = expectation(description: "It should return archives data for a day")
746+
747+
stubRemoteResponse(siteArchivesDataEndpoint, filename: getArchivesDataFilename, contentType: .ApplicationJSON)
748+
749+
let july21 = DateComponents(year: 2025, month: 7, day: 21)
750+
let date = Calendar.autoupdatingCurrent.date(from: july21)!
751+
752+
remote.getData(for: .day, endingOn: date) { (archives: StatsArchiveTimeIntervalData?, error: Error?) in
753+
XCTAssertNil(error)
754+
XCTAssertNotNil(archives)
755+
756+
XCTAssertEqual(archives?.period, .day)
757+
XCTAssertEqual(archives?.periodEndDate, date)
758+
759+
// Test other items
760+
XCTAssertEqual(archives?.summary.other.count, 8)
761+
762+
XCTAssertEqual(archives?.summary.other.first?.href, "http://example.com/wp-admin/admin.php?page=stats")
763+
XCTAssertEqual(archives?.summary.other.first?.value, "/wp-admin/admin.php?page=stats")
764+
XCTAssertEqual(archives?.summary.other.first?.views, 10)
765+
766+
XCTAssertEqual(archives?.summary.other[1].href, "http://example.com/wp-admin/")
767+
XCTAssertEqual(archives?.summary.other[1].value, "/wp-admin/")
768+
XCTAssertEqual(archives?.summary.other[1].views, 4)
769+
770+
XCTAssertEqual(archives?.summary.other.last?.href, "http://example.com/wp-admin/profile.php")
771+
XCTAssertEqual(archives?.summary.other.last?.value, "/wp-admin/profile.php")
772+
XCTAssertEqual(archives?.summary.other.last?.views, 1)
773+
774+
// Test author items
775+
XCTAssertEqual(archives?.summary.author.count, 4)
776+
777+
XCTAssertEqual(archives?.summary.author.first?.href, "http://example.com/author/johndoe/")
778+
XCTAssertEqual(archives?.summary.author.first?.value, "johndoe")
779+
XCTAssertEqual(archives?.summary.author.first?.views, 31)
780+
781+
XCTAssertEqual(archives?.summary.author[1].href, "http://example.com/author/janedoe/")
782+
XCTAssertEqual(archives?.summary.author[1].value, "janedoe")
783+
XCTAssertEqual(archives?.summary.author[1].views, 5)
784+
785+
XCTAssertEqual(archives?.summary.author.last?.href, "http://example.com/author//")
786+
XCTAssertEqual(archives?.summary.author.last?.value, "")
787+
XCTAssertEqual(archives?.summary.author.last?.views, 2)
788+
789+
expect.fulfill()
790+
}
791+
792+
waitForExpectations(timeout: timeout, handler: nil)
793+
}
741794
}

WordPressKit.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
0C1C08412B9CD79900E52F8C /* PostServiceRemoteExtended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1C08402B9CD79900E52F8C /* PostServiceRemoteExtended.swift */; };
2121
0C1C08432B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1C08422B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift */; };
2222
0C1C08452B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1C08442B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift */; };
23+
0C31499B2E2FFBA100AAF9DF /* StatsArchiveTimeIntervalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C31499A2E2FFBA100AAF9DF /* StatsArchiveTimeIntervalData.swift */; };
24+
0C31499D2E2FFBF000AAF9DF /* stats-archives-data.json in Resources */ = {isa = PBXBuildFile; fileRef = 0C31499C2E2FFBF000AAF9DF /* stats-archives-data.json */; };
2325
0C363D422C41B455004E241D /* OCMock in Frameworks */ = {isa = PBXBuildFile; productRef = 0C363D412C41B455004E241D /* OCMock */; };
2426
0C363D452C41B468004E241D /* OHHTTPStubs in Frameworks */ = {isa = PBXBuildFile; productRef = 0C363D442C41B468004E241D /* OHHTTPStubs */; };
2527
0C363D472C41B468004E241D /* OHHTTPStubsSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0C363D462C41B468004E241D /* OHHTTPStubsSwift */; };
@@ -807,6 +809,8 @@
807809
0C1C08402B9CD79900E52F8C /* PostServiceRemoteExtended.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostServiceRemoteExtended.swift; sourceTree = "<group>"; };
808810
0C1C08422B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PostServiceRemoteREST+Extended.swift"; sourceTree = "<group>"; };
809811
0C1C08442B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PostServiceRemoteXMLRPC+Extended.swift"; sourceTree = "<group>"; };
812+
0C31499A2E2FFBA100AAF9DF /* StatsArchiveTimeIntervalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsArchiveTimeIntervalData.swift; sourceTree = "<group>"; };
813+
0C31499C2E2FFBF000AAF9DF /* stats-archives-data.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "stats-archives-data.json"; sourceTree = "<group>"; };
810814
0C3A2A412A2E7BA500FD91D6 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
811815
0C6183C62C420A3700289E73 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
812816
0C674E2F2BF3A91300F3B3D4 /* JetpackAIServiceRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JetpackAIServiceRemote.swift; sourceTree = "<group>"; };
@@ -2284,6 +2288,7 @@
22842288
404057D1221C56AB0060250C /* StatsTopCountryTimeIntervalData.swift */,
22852289
4081976E221DDE9B00A298E4 /* StatsTopPostsTimeIntervalData.swift */,
22862290
404057D9221C9D560060250C /* StatsTopReferrersTimeIntervalData.swift */,
2291+
0C31499A2E2FFBA100AAF9DF /* StatsArchiveTimeIntervalData.swift */,
22872292
404057CD221C38130060250C /* StatsTopVideosTimeIntervalData.swift */,
22882293
);
22892294
path = "Time Interval";
@@ -2609,6 +2614,7 @@
26092614
404057CA221B80BC0060250C /* stats-top-authors.json */,
26102615
404057CF221C46780060250C /* stats-videos-data.json */,
26112616
4081977A221F153A00A298E4 /* stats-visits-day.json */,
2617+
0C31499C2E2FFBF000AAF9DF /* stats-archives-data.json */,
26122618
0CAD70312E2C0AAF00EFD4BC /* stats-visits-hourly.json */,
26132619
01438D372B6A35FB0097D60A /* stats-summary.json */,
26142620
4081977D221F269A00A298E4 /* stats-visits-month.json */,
@@ -3079,6 +3085,7 @@
30793085
B04D8C082BB7895A002717A2 /* stats-insight-streak.json in Resources */,
30803086
74C473B71EF3229B009918F2 /* site-delete-unexpected-json-failure.json in Resources */,
30813087
3297E2852564746800287D21 /* jetpack-scan-unavailable.json in Resources */,
3088+
0C31499D2E2FFBF000AAF9DF /* stats-archives-data.json in Resources */,
30823089
B04D8C072BB7895A002717A2 /* stats-insight-publicize.json in Resources */,
30833090
AB49D0B325D1B4D80084905B /* post-likes-failure.json in Resources */,
30843091
9A2D0B30225E1245009E585F /* jetpack-service-check-site-success-no-jetpack.json in Resources */,
@@ -3376,6 +3383,7 @@
33763383
8BB66DB02523C181000B29DA /* ReaderPostServiceRemote+V2.swift in Sources */,
33773384
74E229501F1E741B0085F7F2 /* RemotePublicizeConnection.swift in Sources */,
33783385
40E7FEB722106A8D0032834E /* StatsCommentsInsight.swift in Sources */,
3386+
0C31499B2E2FFBA100AAF9DF /* StatsArchiveTimeIntervalData.swift in Sources */,
33793387
019C5B8B2BD59CE000A69DB0 /* StatsEmailsSummaryData.swift in Sources */,
33803388
9856BE962630B5C200C12FEB /* RemoteUser+Likes.swift in Sources */,
33813389
3FD634E52BC3A55F00CEDF5E /* WordPressOrgXMLRPCValidator.swift in Sources */,

0 commit comments

Comments
 (0)