blob: e7761ccac61dbf2299817919b770b285be212678 [file] [log] [blame]
Marc Kupietzb74649f2020-12-11 12:44:49 +01001#' Get visits summary from matomo API server
2#'
3#' See matomo Reporting API Reference (\url{https://developer.matomo.org/api-reference/reporting-api}) for details.
4#'
5#' @references \url{https://developer.matomo.org/api-reference/reporting-api}
6#'
7#' @param matomoUrl base URL of your matomo instance
8#' @param siteId matomo site id or vector of site ids
9#' @param period \code{day}, \code{week}, \code{month} or \code{year}
10#' @param date date range (see \url{https://developer.matomo.org/api-reference/reporting-api})
11#' @param filter_limit defines the maximum number of rows to be returned
Marc Kupietzb74649f2020-12-11 12:44:49 +010012#' @param removeFirst logical that determines whether the first row of each site should be removed (to account for incomplete periods)
13#' @param removeLast logical that determines whether the last row of each site should be removed (to account for incomplete periods)
Marc Kupietz7d4149f2020-12-14 08:41:45 +010014#' @param accessToken API Authentication Token - you can get this in your
15#' matomo interface under Settings -> Personal -> Settings -> API Authentication Token
Marc Kupietzb74649f2020-12-11 12:44:49 +010016#' and pass it here, or you can make it persistent with \code{\link{persistAccessToken}}.
Marc Kupietz7d4149f2020-12-14 08:41:45 +010017#' @return Data frame with visits summary as returned by matomo. Note that the \code{date} column in the returned data frame refers to the first day of the respective period.
Marc Kupietzb74649f2020-12-11 12:44:49 +010018#'
19#' @import httr
20#' @importFrom jsonlite fromJSON
21#' @importFrom dplyr mutate rowwise bind_rows select summarise n
22#' @import tibble
23#' @importFrom magrittr %>%
24#' @importFrom utils head tail
25#'
26#' @examples
27#' \dontrun{
28#' df <- getVisitsSummary("https://demo.matomo.org/", siteId=3, period="day", date="last60")
29#' plot(df$nb_actions ~ df$date, type="b")
30#' }
31#'
32#' @export
33getVisitsSummary <- function(matomoUrl,
34 siteId,
35 period = "month",
36 date = "last16",
37 filter_limit = 100,
38 removeFirst = FALSE,
39 removeLast = FALSE,
40 accessToken = getAccessToken(matomoUrl)
41
42) {
43 if (is.null(accessToken) && matomoUrl != "https://demo.matomo.org/") {
44 stop(
45 paste0(
Marc Kupietzd43abe92021-03-24 12:26:03 +010046 "You must first set an access token with:\n\npersistAccessToken(\"",
Marc Kupietzb74649f2020-12-11 12:44:49 +010047 matomoUrl,
Marc Kupietzd43abe92021-03-24 12:26:03 +010048 "\", <token>)\n\nYou can get the token in your matomo interface under Settings -> Personal -> Security -> Auth token\nprobably at:\n\n", matomoUrl, "index.php?module=UsersManager&action=userSecurity\n\n"
Marc Kupietzb74649f2020-12-11 12:44:49 +010049 ),
50 call. = FALSE
51 )
52 }
53
54 httr::GET(
55 url = matomoUrl,
56 query = list(
57 module = "API",
58 method = "API.getBulkRequest",
59 format = "json",
60 "urls[0]" = "method=VisitsSummary.get",
61 idSite = paste0(siteId, collapse = ","),
62 date = date,
63 period = period,
64 filter_limit = filter_limit,
65 token_auth = accessToken
66 )
67 ) -> res
68
69 if (status_code(res) != 200) {
70 if (json && !http_type(res) %in% c("application/json", "application/ld+json")) {
71 stop("API did not return json", call. = FALSE)
72 }
73 result <- jsonlite::fromJSON(content(res, "text", encoding = "UTF-8"))
74 if (!is.null(result$warnings)) {
75 message <- if (nrow(result$warnings) > 1)
76 sapply(result$warnings, function(warning) paste(sprintf("%s: %s", warning[1], warning[2]), sep="\n"))
77 else
78 sprintf("%s: %s", result$warnings[1], result$warnings[2])
79 warning(message, call. = FALSE)
80 }
81 }
82
83 if (!http_type(res) %in% c("application/json", "application/ld+json")) {
84 stop("API did not return json", call. = FALSE)
85 }
86
87 json <- httr::content(res, "text", encoding = "UTF-8")
88 l <- jsonlite::fromJSON(json, simplifyVector = F)
89
90 if (!is.null(l[[1]]$result) && l[[1]]$result == "error") {
91 stop(l[[1]]$message, call. = FALSE)
92 }
93
94 (if (length(siteId) == 1) {
95 bind_rows(l[[1]], .id=period) %>%
96 head(if(removeLast) -1 else filter_limit) %>%
97 tail(if(removeFirst) -1 else filter_limit) %>%
98 mutate(site_id=siteId)
99 } else {
100 df <- bind_rows(l[[1]][[1]], .id=period) %>%
101 head(if(removeLast) -1 else filter_limit) %>%
102 tail(if(removeFirst) -1 else filter_limit) %>%
103 mutate(site_id=siteId[1])
104 for (i in 2:length(l[[1]])) {
105 df <- bind_rows(df,
106 bind_rows(l[[1]][[i]], .id=period) %>%
107 head(if(removeLast) -1 else filter_limit) %>%
108 tail(if(removeFirst) -1 else filter_limit) %>%
109 mutate(site_id=siteId[i]))
110 }
111 df
112 }) %>%
113 mutate(date = as.Date(
114 if (period == "month")
115 paste0(month, "-01")
116 else if (period == "year")
117 paste0(year, "-01-01")
118 else if (period == "week")
119 sub(",.*", "", week)
120 else if (period == "day")
121 day
122 else
123 stop(paste0("unsupported period parameter: '", period, "'"), call. = FALSE)
124 ))
125}
126
127utils::globalVariables(c("year", "month", "day", "week"))
128
129#' Save access token persistently to your keyring
130#'
131#' @param matomoUrl base URL of your matomo instance
132#' @param accessToken your oauth token
133#' @param id supply if you have multiple IDs, i.e. logins to your matomo instance
134#'
135#' @import keyring
136#'
137#' @export
138#'
139#' @examples
140#' persistAccessToken("https://demo.matomo.org/", "ad7609a669179c4ebca7c995342f7e09")
141#'
142persistAccessToken <- function(matomoUrl, accessToken, id="default") {
143 if (is.null(accessToken))
144 stop("It seems that you have not supplied any access token that could be persisted.", call. = FALSE)
145
146 keyring::key_set_with_value(matomoUrl, username=id, password=accessToken, keyring = NULL)
147}
148
149clearAccessToken <- function(matomoUrl, id="default") {
150 key_delete(matomoUrl, id)
151}
152
153
154
155#' get access token for matomo from keyring
156#'
157#' @param matomoUrl base URL of your matomo instance
158#' @param id supply if you have multiple IDs, i.e. logins to your matomo instance
159#'
160#' @return access token
161#' @export
162#'
163#' @import keyring
164#'
165getAccessToken <- function(matomoUrl, id="default") {
166 keyList <- tryCatch(withCallingHandlers(key_list(service = matomoUrl),
167 warning = function(w) invokeRestart("muffleWarning"),
168 error = function(e) return(NULL)),
169 error = function(e) { })
170 if (id %in% keyList)
171 key_get(matomoUrl, id)
172 else
173 NULL
174}
175
176