R/twCompliance.R

Defines functions list_compliance_jobs report_compliance download_compliance_check start_compliance_check

Documented in download_compliance_check list_compliance_jobs report_compliance start_compliance_check

#' Start a compliance check
#'
#' @return twCompliance ID of the compliance job. Save this to check the jobs status and download the results.
#'
#' @param data A dataframe generated by `combine_status()`.
#'
#' @importFrom usethis ui_info ui_line
#' @export
start_compliance_check <- function(data) {
    return_list <- list("tweets"=NULL, "users"=NULL, "data"=data, "checked_at"=lubridate::now())

    var_list <- names(data)

    if (!all(var_list %in% c("status_id", "user_id", "created_at", "account_created_at"))) {
        usethis::ui_oops("Data must only contain any/all of {usethis::ui_field('status_id')}, {usethis::ui_field('user_id')}, {usethis::ui_field('created_at')}, {usethis::ui_field('account_created_at')}")
        stop("Wrong format")
    }

    if (!any(c("status_id", "user_id") %in% var_list)) {
        stop("Data has the wrong format, status_id and/or user_id are required")
    }

    if (all(c("created_at", "account_created_at") %in% var_list)) {
        if (!lubridate::is.POSIXct(data$created_at) &
            !lubridate::is.POSIXct(data$account_created_at)) {
            usethis::ui_oops("The variables {usethis::ui_field('created_at')} and {usethis::ui_field('account_created_at')} must both be of type POSIXct.")
            usethis::ui_info("Only {usethis::ui_field('status_id')} and {usethis::ui_field('user_id')} will be used.")
        }
    } else if ("created_at" %in% var_list) {
        if (!lubridate::is.POSIXct(data$created_at)) {
            usethis::ui_oops("The variable {usethis::ui_field('created_at')} must be of type POSIXct.")
            usethis::ui_info("Only {usethis::ui_field('status_id')} and {usethis::ui_field('user_id')} will be used.")
        }
    }  else if ("account_created_at" %in% var_list) {
        if (!lubridate::is.POSIXct(data$account_created_at)) {
            usethis::ui_oops("The variable {usethis::ui_field('account_created_at')} must be of type POSIXct.")
            usethis::ui_info("Only {usethis::ui_field('status_id')} and {usethis::ui_field('user_id')} will be used.")
        }
    }

    usethis::ui_info("Starting compliance check and uploading data (this might take a while)")
    if ("status_id" %in% var_list) {
        r <- httr::GET(
            sprintf("https://api.twitter.com/2/compliance/jobs?type=%s", "tweets"),
            httr::add_headers(
                "Authorization" = paste0("Bearer ", get_bearer())
            )
        )
        if ("in_progress" %in% (unlist(lapply(httr::content(r)$data, function(x){x[["status"]]})))) {
            usethis::ui_oops("Cannot start a new job with type 'tweets', as there is an ongoing job with that type")
            #list_compliance_jobs(type = "tweets")
            stop("Check in progress")
        }
        return_list$tweets <- start_job(data$status_id, type = "tweets", verbose = FALSE)
    }
    if ("user_id" %in% var_list) {
        r <- httr::GET(
            sprintf("https://api.twitter.com/2/compliance/jobs?type=%s", "users"),
            httr::add_headers(
                "Authorization" = paste0("Bearer ", get_bearer())
            )
        )
        if ("in_progress" %in% (unlist(lapply(httr::content(r)$data, function(x){x[["status"]]})))) {
            usethis::ui_oops("Cannot start a new job with type 'users', as there is an ongoing job with that type")
            #list_compliance_jobs(type = "users")
            stop("Check in progress")
        }
        return_list$users <- start_job(data$user_id, type = "users", verbose = FALSE)
    }

    return_list$print_string <- gen_print(return_list)

    success_msg <- c("Compliance check started successfully:")
    if ("status_id" %in% names(return_list$data)) {
        n_tweets <- format(length(unique(return_list$data$status_id)), big.mark = ",")
        success_msg <- c(success_msg, "\n\t{usethis::ui_field('Tweets')}: {usethis::ui_value(n_tweets)}")
    }
    if ("user_id" %in% names(return_list$data)) {
        n_users <- format(length(unique(return_list$data$user_id)), big.mark = ",")
        success_msg <- c(success_msg, "\n\t{usethis::ui_field('Users')}: {usethis::ui_value(n_users)}")
    }
    usethis::ui_done(paste0(success_msg, collapse = ""))

    return(structure(return_list, class = "twCompliance"))
}

#' Download a compliacnce check
#'
#' This function downloads a compliance check
#'
#' @return data.frame The downloaded and combinded compliance check
#'
#' @param job_id The object generated by `start_compliance_check()`.
#' @param return_raw bool Should a combinded output be returned or the raw API response?
#'
#' @importFrom usethis ui_info ui_line
#' @export
download_compliance_check <- function(
        job_id,
        return_raw = FALSE
) {
    downloaded_jobs <- check_job(job_id, verbose = FALSE)
    if (is.character(downloaded_jobs$tweets) & is.character(downloaded_jobs$users)) {
        usethis::ui_info("Downloading jobs")
        downloaded_jobs$users <- download_job(job_id$users)
        downloaded_jobs$tweets <- download_job(job_id$tweets)
        if (return_raw) {
            return(downloaded_jobs)
        }
        combinded_data <- combine_jobs(job_id$data, downloaded_jobs$tweets, downloaded_jobs$users)
        combinded_data$checked_at <- job_id$checked_at
        return(combinded_data)
    } else if (is.character(downloaded_jobs$tweets) & is.null(job_id$users)) {
        usethis::ui_info("Downloading jobs")
        downloaded_jobs$users <- NULL
        downloaded_jobs$tweets <- download_job(job_id$tweets)
        if (return_raw) {
            return(downloaded_jobs)
        }
        combinded_data <- combine_jobs(job_id$data, downloaded_jobs$tweets, downloaded_jobs$users)
        combinded_data$checked_at <- job_id$checked_at
        return(combinded_data)
    } else if (is.character(downloaded_jobs$users) & is.null(job_id$tweets)) {
        usethis::ui_info("Downloading jobs")
        downloaded_jobs$users <- download_job(job_id$users)
        downloaded_jobs$tweets <- NULL
        if (return_raw) {
            return(downloaded_jobs)
        }
        combinded_data <- combine_jobs(job_id$data, downloaded_jobs$tweets, downloaded_jobs$users)
        combinded_data$checked_at <- job_id$checked_at
        return(combinded_data)
    } else {
        usethis::ui_info("Jobs are not ready yet\n\n")
        if (!is.null(job_id$tweets)) {check_job(job_id$tweets)}
        if (!is.null(job_id$users)) {check_job(job_id$users)}
    }
}

#' Report deleted content
#'
#' This function generates a report of deleted content
#'
#' @return string ID of the compliance job. Save this to check the jobs status and download the results.
#'
#' @param df A dataframe generated by `combine_status()`.
#' @param plot If `TRUE` plot a report as well.
#' @param combine_plots If `TRUE` plots will be combined with `patchwork`, if `FALSE` a named list of plots will be returned.
#'
#' @importFrom usethis ui_info ui_line
#' @export
report_compliance <- function(df, plot = FALSE, combine_plots = TRUE) {
    if ("status_id" %in% names(df)) {
        cat(report_text(df, type = "tweets"))
        if (plot == TRUE) {
            if ("created_at" %in% names(df)) {
                report_plot(df, type = "tweets", combine_plots = combine_plots)
            } else {
                usethis::ui_oops("Plots require {usethis::ui_field('created_at')} in data")
            }
        }
    } else if ("user_id" %in% names(df)) {
        cat(report_text(df, type = "users"))
        if (plot == TRUE) {
            if ("account_created_at" %in% names(df)) {
                report_plot(df, type = "users", combine_plots = combine_plots)
            } else {
                usethis::ui_oops("Plots require {usethis::ui_field('account_created_at')} in data")
            }
        }
    } else if (!all(c("status_id", "user_id") %in% names(df))) {
        usethis::ui_oops("Wrong format ({usethis::ui_field('status_id')} or {usethis::ui_field('user_id')} are required)")
    }
}

#' List all compliance jobs
#'
#' This function prints a list of all compliance jobs.
#'
#' @param bearer_token API bearer token.
#' @param type Type of the job. Either 'all' (default), 'tweets', or 'users'.
#' @param status The status of the job. Either 'all' (default), 'in_progress', or 'complete'.
#'
#' @export
list_compliance_jobs <- function(type = "all",
                      status = "all",
                      bearer_token = get_bearer()) {
    if (!type %in% c("users", "tweets", "all")) {
        stop("Wrong type")
    }
    if (!status %in% c("in_progress", "complete", "all")) {
        stop("Wrong status")
    }
    if (type == "all") {
        type <- c("tweets", "users")
    }
    for (ct in type) {
        r <- httr::GET(
            sprintf(
                "https://api.twitter.com/2/compliance/jobs?type=%s",
                ct
            ),
            httr::add_headers("Authorization" = paste0("Bearer ", bearer_token))
        )
        jobs <- httr::content(r)$data

        if (status != "all") {
            jobs <- jobs[unlist(lapply(jobs, `[`, "status")) == status]
        }
        if (httr::status_code(r) == 200) {
            usethis::ui_info(
                "{length(jobs)} jobs(s) of type {usethis::ui_value(ct)} and status {usethis::ui_value(status)} found"
            )
            for (job in jobs) {
                print_job(job)
            }
        }
    }

}
Kudusch/twCompliance documentation built on July 10, 2024, 3:53 p.m.