Add getUserCountry and general API query function matomoQuery

Change-Id: Iedea1fcacf7329144e71f1412fc41238efc30bb2
diff --git a/Changelog.md b/Changelog.md
index 46ea02a..544610f 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,7 @@
 # Changelog
 
 ## 0.1.9000
+* adds function `getUserCountry`
+* adds general API query function `matomoQuery`
+
+
diff --git a/DESCRIPTION b/DESCRIPTION
index c89a8db..5ecab87 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -30,5 +30,7 @@
 Suggests:
     testthat
 Collate: 
+    'matomoquery.R'
     'visitssummary.R'
+    'usercountry.R'
     'reexports.R'
diff --git a/NAMESPACE b/NAMESPACE
index 6dd0595..144768c 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -6,8 +6,10 @@
 export(complete)
 export(expand_grid)
 export(getAccessToken)
+export(getUserCountry)
 export(getVisitsSummary)
 export(group_by)
+export(matomoQuery)
 export(mutate)
 export(n)
 export(persistAccessToken)
diff --git a/R/matomoquery.R b/R/matomoquery.R
new file mode 100644
index 0000000..ed89b2e
--- /dev/null
+++ b/R/matomoquery.R
@@ -0,0 +1,181 @@
+#' Get reports from a matomo API server
+#'
+#' See matomo Reporting API Reference (\url{https://developer.matomo.org/api-reference/reporting-api}) for details.
+#'
+#' @references \url{https://developer.matomo.org/api-reference/reporting-api}
+#'
+#' @param matomoUrl    base URL of your matomo instance
+#' @param siteId       matomo site id or vector of site ids
+#' @param period       \code{day}, \code{week}, \code{month} or \code{year}
+#' @param date         date range (see \url{https://developer.matomo.org/api-reference/reporting-api})
+#' @param filter_limit defines the maximum number of rows to be returned
+#' @param removeFirst  logical that determines whether the first row of each site should be removed (to account for incomplete periods)
+#' @param removeLast   logical that determines whether the last row of each site should be removed (to account for incomplete periods)
+#' @param accessToken  API Authentication Token - you can get this in your
+#' matomo interface under Settings -> Personal -> Settings -> API Authentication Token
+#' and pass it here, or you can make it persistent with \code{\link{persistAccessToken}}.
+#' @param getMethod    API method to call – default: VisitsSummary.get
+#' @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.
+#'
+#' @import httr
+#' @importFrom jsonlite fromJSON
+#' @importFrom dplyr mutate rowwise bind_rows select summarise n
+#' @import tibble
+#' @importFrom magrittr %>%
+#' @importFrom utils head tail
+#'
+#' @examples
+#' \dontrun{
+#' df <- matomoQuery("https://demo.matomo.org/", getMethod = "UserCountry.getCountry")
+#' }
+#'
+#' @export
+matomoQuery <- function(matomoUrl,
+                             siteId,
+                             period = "month",
+                             date = "last16",
+                             filter_limit = 100,
+                             removeFirst = FALSE,
+                             removeLast = FALSE,
+                             accessToken = getAccessToken(matomoUrl),
+                             getMethod = "VisitsSummary.get"
+) {
+  if (is.null(accessToken) && matomoUrl != "https://demo.matomo.org/") {
+    stop(
+      paste0(
+        "You must first set an access token with:\n\npersistAccessToken(\"",
+        matomoUrl,
+        "\", <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"
+      ),
+      call. = FALSE
+    )
+  }
+
+  httr::GET(
+    url = matomoUrl,
+    query = list(
+      module = "API",
+      method = "API.getBulkRequest",
+      format = "json",
+      "urls[0]" = paste0("method=", getMethod),
+      idSite = paste0(siteId, collapse = ","),
+      date = date,
+      period = period,
+      filter_limit = filter_limit,
+      token_auth = accessToken
+    )
+  ) -> res
+
+  if (status_code(res) != 200) {
+    if (json && !http_type(res) %in% c("application/json", "application/ld+json")) {
+      stop("API did not return json", call. = FALSE)
+    }
+    result <- jsonlite::fromJSON(content(res, "text", encoding = "UTF-8"))
+    if (!is.null(result$warnings)) {
+      message <- if (nrow(result$warnings) > 1)
+        sapply(result$warnings, function(warning) paste(sprintf("%s: %s", warning[1], warning[2]), sep="\n"))
+      else
+        sprintf("%s: %s", result$warnings[1], result$warnings[2])
+      warning(message, call. = FALSE)
+    }
+  }
+
+  if (!http_type(res) %in% c("application/json", "application/ld+json")) {
+    stop("API did not return json", call. = FALSE)
+  }
+
+  json <- httr::content(res, "text", encoding = "UTF-8")
+  l <- jsonlite::fromJSON(json, simplifyVector = F)
+
+  if (!is.null(l[[1]]$result) && l[[1]]$result == "error") {
+    stop(l[[1]]$message, call. = FALSE)
+  }
+
+  df <- (if (length(siteId) == 1) {
+    bind_rows(l[[1]], .id=period) %>%
+      head(if(removeLast) -1 else filter_limit) %>%
+      tail(if(removeFirst) -1 else filter_limit) %>%
+      mutate(site_id=siteId)
+  } else {
+    df <- bind_rows(l[[1]][[1]], .id=period) %>%
+      head(if(removeLast) -1 else filter_limit) %>%
+      tail(if(removeFirst) -1 else filter_limit) %>%
+      mutate(site_id=siteId[1])
+    for (i in 2:length(l[[1]])) {
+      df <- bind_rows(df,
+                      bind_rows(l[[1]][[i]], .id=period) %>%
+                        head(if(removeLast) -1 else filter_limit) %>%
+                        tail(if(removeFirst) -1 else filter_limit) %>%
+                        mutate(site_id=siteId[i]))
+    }
+    df
+  })
+
+  if("day" %in% colnames(df) | "month" %in% colnames(df) | "ye" %in% colnames(df)) {
+    df <- df %>%
+      mutate(date = as.Date(
+        if (period == "month")
+          paste0(month, "-01")
+        else if (period == "year")
+          paste0(year, "-01-01")
+        else if (period == "week")
+          sub(",.*", "", week)
+        else if (period == "day")
+          day
+        else
+          stop(paste0("unsupported period parameter: '", period, "'"), call. = FALSE)
+      ))
+  }
+  return(df)
+}
+
+utils::globalVariables(c("year", "month", "day", "week"))
+
+#' Save access token persistently to your keyring
+#'
+#' @param matomoUrl   base URL of your matomo instance
+#' @param accessToken your oauth token
+#' @param id          supply if you have multiple IDs, i.e. logins to your matomo instance
+#'
+#' @import keyring
+#'
+#' @export
+#'
+#' @examples
+#' persistAccessToken("https://demo.matomo.org/", "ad7609a669179c4ebca7c995342f7e09")
+#'
+persistAccessToken <- function(matomoUrl, accessToken, id="default") {
+  if (is.null(accessToken))
+    stop("It seems that you have not supplied any access token that could be persisted.", call. = FALSE)
+
+  keyring::key_set_with_value(matomoUrl, username=id, password=accessToken, keyring = NULL)
+}
+
+clearAccessToken <- function(matomoUrl, id="default") {
+  key_delete(matomoUrl, id)
+}
+
+
+
+#' get access token for matomo from keyring
+#'
+#' @param matomoUrl   base URL of your matomo instance
+#' @param id          supply if you have multiple IDs, i.e. logins to your matomo instance
+#'
+#' @return access token
+#' @export
+#'
+#' @import keyring
+#'
+getAccessToken <- function(matomoUrl, id="default") {
+  keyList <- tryCatch(withCallingHandlers(key_list(service = matomoUrl),
+                                          warning = function(w) invokeRestart("muffleWarning"),
+                                          error = function(e) return(NULL)),
+                      error = function(e) { })
+  if (id %in% keyList)
+    key_get(matomoUrl, id)
+  else
+    NULL
+}
+
+
diff --git a/R/usercountry.R b/R/usercountry.R
new file mode 100644
index 0000000..9eb3638
--- /dev/null
+++ b/R/usercountry.R
@@ -0,0 +1,30 @@
+#' Get user country report from matomo API server
+#'
+#' See matomo Reporting API Reference (\url{https://developer.matomo.org/api-reference/reporting-api}) for details.
+#'
+#' @references \url{https://developer.matomo.org/api-reference/reporting-api}
+#'
+#' @inheritParams matomoQuery
+#'
+#' @examples
+#' \dontrun{
+#' df <- getUserCountry("https://demo.matomo.org/", siteId=3, period="day", date="last60")
+#' }
+#'
+#' @export
+getUserCountry <- function(matomoUrl,
+                             siteId,
+                             period = "month",
+                             date = "last36",
+                             filter_limit = 100,
+                             accessToken = getAccessToken(matomoUrl)
+) {
+  matomoQuery(matomoUrl = matomoUrl,
+              siteId = siteId,
+              period = period,
+              date = date,
+              removeFirst = FALSE,
+              removeLast = FALSE,
+              accessToken = accessToken,
+              getMethod = "UserCountry.getCountry")
+}
diff --git a/R/visitssummary.R b/R/visitssummary.R
index e7761cc..c1f517d 100644
--- a/R/visitssummary.R
+++ b/R/visitssummary.R
@@ -4,24 +4,7 @@
 #'
 #' @references \url{https://developer.matomo.org/api-reference/reporting-api}
 #'
-#' @param matomoUrl    base URL of your matomo instance
-#' @param siteId       matomo site id or vector of site ids
-#' @param period       \code{day}, \code{week}, \code{month} or \code{year}
-#' @param date         date range (see \url{https://developer.matomo.org/api-reference/reporting-api})
-#' @param filter_limit defines the maximum number of rows to be returned
-#' @param removeFirst  logical that determines whether the first row of each site should be removed (to account for incomplete periods)
-#' @param removeLast   logical that determines whether the last row of each site should be removed (to account for incomplete periods)
-#' @param accessToken  API Authentication Token - you can get this in your
-#' matomo interface under Settings -> Personal -> Settings -> API Authentication Token
-#' and pass it here, or you can make it persistent with \code{\link{persistAccessToken}}.
-#' @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.
-#'
-#' @import httr
-#' @importFrom jsonlite fromJSON
-#' @importFrom dplyr mutate rowwise bind_rows select summarise n
-#' @import tibble
-#' @importFrom magrittr %>%
-#' @importFrom utils head tail
+#' @inheritParams matomoQuery
 #'
 #' @examples
 #' \dontrun{
@@ -38,139 +21,13 @@
                             removeFirst = FALSE,
                             removeLast = FALSE,
                             accessToken = getAccessToken(matomoUrl)
-
 ) {
-  if (is.null(accessToken) && matomoUrl != "https://demo.matomo.org/") {
-    stop(
-      paste0(
-        "You must first set an access token with:\n\npersistAccessToken(\"",
-        matomoUrl,
-        "\", <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"
-      ),
-      call. = FALSE
-    )
-  }
-
-  httr::GET(
-    url = matomoUrl,
-    query = list(
-      module = "API",
-      method = "API.getBulkRequest",
-      format = "json",
-      "urls[0]" = "method=VisitsSummary.get",
-      idSite = paste0(siteId, collapse = ","),
-      date = date,
-      period = period,
-      filter_limit = filter_limit,
-      token_auth = accessToken
-    )
-  ) -> res
-
-  if (status_code(res) != 200) {
-    if (json && !http_type(res) %in% c("application/json", "application/ld+json")) {
-      stop("API did not return json", call. = FALSE)
-    }
-    result <- jsonlite::fromJSON(content(res, "text", encoding = "UTF-8"))
-    if (!is.null(result$warnings)) {
-      message <- if (nrow(result$warnings) > 1)
-        sapply(result$warnings, function(warning) paste(sprintf("%s: %s", warning[1], warning[2]), sep="\n"))
-      else
-        sprintf("%s: %s", result$warnings[1], result$warnings[2])
-      warning(message, call. = FALSE)
-    }
-  }
-
-  if (!http_type(res) %in% c("application/json", "application/ld+json")) {
-    stop("API did not return json", call. = FALSE)
-  }
-
-  json <- httr::content(res, "text", encoding = "UTF-8")
-  l <- jsonlite::fromJSON(json, simplifyVector = F)
-
-  if (!is.null(l[[1]]$result) && l[[1]]$result == "error") {
-    stop(l[[1]]$message, call. = FALSE)
-  }
-
-  (if (length(siteId) == 1) {
-    bind_rows(l[[1]], .id=period) %>%
-      head(if(removeLast) -1 else filter_limit) %>%
-      tail(if(removeFirst) -1 else filter_limit) %>%
-      mutate(site_id=siteId)
-  } else {
-    df <- bind_rows(l[[1]][[1]], .id=period) %>%
-      head(if(removeLast) -1 else filter_limit) %>%
-      tail(if(removeFirst) -1 else filter_limit) %>%
-      mutate(site_id=siteId[1])
-    for (i in 2:length(l[[1]])) {
-      df <- bind_rows(df,
-                      bind_rows(l[[1]][[i]], .id=period) %>%
-                      head(if(removeLast) -1 else filter_limit) %>%
-                      tail(if(removeFirst) -1 else filter_limit) %>%
-                      mutate(site_id=siteId[i]))
-    }
-    df
-  }) %>%
-    mutate(date = as.Date(
-      if (period == "month")
-        paste0(month, "-01")
-      else if (period == "year")
-        paste0(year, "-01-01")
-      else if (period == "week")
-        sub(",.*", "", week)
-      else if (period == "day")
-        day
-      else
-        stop(paste0("unsupported period parameter: '", period, "'"), call. = FALSE)
-    ))
+  matomoQuery(matomoUrl = matomoUrl,
+              siteId = siteId,
+              period = period,
+              date = date,
+              removeFirst = removeFirst,
+              removeLast = removeLast,
+              accessToken = accessToken,
+              getMethod = "VisitsSummary.get")
 }
-
-utils::globalVariables(c("year", "month", "day", "week"))
-
-#' Save access token persistently to your keyring
-#'
-#' @param matomoUrl   base URL of your matomo instance
-#' @param accessToken your oauth token
-#' @param id          supply if you have multiple IDs, i.e. logins to your matomo instance
-#'
-#' @import keyring
-#'
-#' @export
-#'
-#' @examples
-#' persistAccessToken("https://demo.matomo.org/", "ad7609a669179c4ebca7c995342f7e09")
-#'
-persistAccessToken <- function(matomoUrl, accessToken, id="default") {
-  if (is.null(accessToken))
-    stop("It seems that you have not supplied any access token that could be persisted.", call. = FALSE)
-
-  keyring::key_set_with_value(matomoUrl, username=id, password=accessToken, keyring = NULL)
-}
-
-clearAccessToken <- function(matomoUrl, id="default") {
-  key_delete(matomoUrl, id)
-}
-
-
-
-#' get access token for matomo from keyring
-#'
-#' @param matomoUrl   base URL of your matomo instance
-#' @param id          supply if you have multiple IDs, i.e. logins to your matomo instance
-#'
-#' @return access token
-#' @export
-#'
-#' @import keyring
-#'
-getAccessToken <- function(matomoUrl, id="default") {
-  keyList <- tryCatch(withCallingHandlers(key_list(service = matomoUrl),
-                                          warning = function(w) invokeRestart("muffleWarning"),
-                                          error = function(e) return(NULL)),
-                      error = function(e) { })
-  if (id %in% keyList)
-    key_get(matomoUrl, id)
-  else
-    NULL
-}
-
-
diff --git a/man/getAccessToken.Rd b/man/getAccessToken.Rd
index b50b328..830b414 100644
--- a/man/getAccessToken.Rd
+++ b/man/getAccessToken.Rd
@@ -1,5 +1,5 @@
 % Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/visitssummary.R
+% Please edit documentation in R/matomoquery.R
 \name{getAccessToken}
 \alias{getAccessToken}
 \title{get access token for matomo from keyring}
diff --git a/man/getUserCountry.Rd b/man/getUserCountry.Rd
new file mode 100644
index 0000000..d9fa90d
--- /dev/null
+++ b/man/getUserCountry.Rd
@@ -0,0 +1,42 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/usercountry.R
+\name{getUserCountry}
+\alias{getUserCountry}
+\title{Get user country report from matomo API server}
+\usage{
+getUserCountry(
+  matomoUrl,
+  siteId,
+  period = "month",
+  date = "last36",
+  filter_limit = 100,
+  accessToken = getAccessToken(matomoUrl)
+)
+}
+\arguments{
+\item{matomoUrl}{base URL of your matomo instance}
+
+\item{siteId}{matomo site id or vector of site ids}
+
+\item{period}{\code{day}, \code{week}, \code{month} or \code{year}}
+
+\item{date}{date range (see \url{https://developer.matomo.org/api-reference/reporting-api})}
+
+\item{filter_limit}{defines the maximum number of rows to be returned}
+
+\item{accessToken}{API Authentication Token - you can get this in your
+matomo interface under Settings -> Personal -> Settings -> API Authentication Token
+and pass it here, or you can make it persistent with \code{\link{persistAccessToken}}.}
+}
+\description{
+See matomo Reporting API Reference (\url{https://developer.matomo.org/api-reference/reporting-api}) for details.
+}
+\examples{
+\dontrun{
+df <- getUserCountry("https://demo.matomo.org/", siteId=3, period="day", date="last60")
+}
+
+}
+\references{
+\url{https://developer.matomo.org/api-reference/reporting-api}
+}
diff --git a/man/getVisitsSummary.Rd b/man/getVisitsSummary.Rd
index 7ce6eea..2ec475a 100644
--- a/man/getVisitsSummary.Rd
+++ b/man/getVisitsSummary.Rd
@@ -34,9 +34,6 @@
 matomo interface under Settings -> Personal -> Settings -> API Authentication Token
 and pass it here, or you can make it persistent with \code{\link{persistAccessToken}}.}
 }
-\value{
-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.
-}
 \description{
 See matomo Reporting API Reference (\url{https://developer.matomo.org/api-reference/reporting-api}) for details.
 }
diff --git a/man/matomoQuery.Rd b/man/matomoQuery.Rd
new file mode 100644
index 0000000..87501c1
--- /dev/null
+++ b/man/matomoQuery.Rd
@@ -0,0 +1,54 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/matomoquery.R
+\name{matomoQuery}
+\alias{matomoQuery}
+\title{Get reports from a matomo API server}
+\usage{
+matomoQuery(
+  matomoUrl,
+  siteId,
+  period = "month",
+  date = "last16",
+  filter_limit = 100,
+  removeFirst = FALSE,
+  removeLast = FALSE,
+  accessToken = getAccessToken(matomoUrl),
+  getMethod = "VisitsSummary.get"
+)
+}
+\arguments{
+\item{matomoUrl}{base URL of your matomo instance}
+
+\item{siteId}{matomo site id or vector of site ids}
+
+\item{period}{\code{day}, \code{week}, \code{month} or \code{year}}
+
+\item{date}{date range (see \url{https://developer.matomo.org/api-reference/reporting-api})}
+
+\item{filter_limit}{defines the maximum number of rows to be returned}
+
+\item{removeFirst}{logical that determines whether the first row of each site should be removed (to account for incomplete periods)}
+
+\item{removeLast}{logical that determines whether the last row of each site should be removed (to account for incomplete periods)}
+
+\item{accessToken}{API Authentication Token - you can get this in your
+matomo interface under Settings -> Personal -> Settings -> API Authentication Token
+and pass it here, or you can make it persistent with \code{\link{persistAccessToken}}.}
+
+\item{getMethod}{API method to call – default: VisitsSummary.get}
+}
+\value{
+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.
+}
+\description{
+See matomo Reporting API Reference (\url{https://developer.matomo.org/api-reference/reporting-api}) for details.
+}
+\examples{
+\dontrun{
+df <- matomoQuery("https://demo.matomo.org/", getMethod = "UserCountry.getCountry")
+}
+
+}
+\references{
+\url{https://developer.matomo.org/api-reference/reporting-api}
+}
diff --git a/man/persistAccessToken.Rd b/man/persistAccessToken.Rd
index 9d27b51..8386c0b 100644
--- a/man/persistAccessToken.Rd
+++ b/man/persistAccessToken.Rd
@@ -1,5 +1,5 @@
 % Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/visitssummary.R
+% Please edit documentation in R/matomoquery.R
 \name{persistAccessToken}
 \alias{persistAccessToken}
 \title{Save access token persistently to your keyring}