在Microsoft Dynamics中,如果您单击“高级视图”筛选器,则可以选择所需的数据并让系统自动生成fetchXML。我创建了一个R脚本,使用他们提供的Web API从Dynamics中提取数据。
整个脚本都在下面发布,希望你们中的一些人会发现它有用。很难过分强调这个Web API有多难以使用,找出所有这些内容是一个巨大的痛苦。
我在企业环境中工作,因此您将看到许多关于通过代理进行身份验证等代码。
![enter image description here](https://istack.dev59.com/kni3U.webp)
options(stringsAsFactors = FALSE)
options(httr_oauth_cache=T)
client_id <- "YOUR ID"
app_name <- "MY_APP"
client_secret <- "YOUR SECRET"
application_id_uri = "YOUR URI "
if(require(librarian) == FALSE){
install.packages("librarian")
if(require(librarian)== FALSE){stop("Unable to install and load librarian")}
}
librarian::shelf(tidyverse, readxl, RODBC, lubridate, httr, XML, jsonlite, rlist, httpuv, quiet = TRUE)
azure_endpoint <- oauth_endpoints("azure")
myapp <- oauth_app(
appname = app_name,
key = client_id,
secret = client_secret
)
mytoken <- oauth2.0_token(azure_endpoint,
myapp,
scope = application_id_uri,
cache = str_cache
)
if (("error" %in% names(mytoken$credentials)) && (nchar(mytoken$credentials$error) > 0)) {
errorMsg <- paste("Error while acquiring token.",
paste("Error message:", mytoken$credentials$error),
paste("Error description:", mytoken$credentials$error_description),
paste("Error code:", mytoken$credentials$error_codes),
sep = "\n"
)
stop(errorMsg)
}
base_url <- "https://YOURPLATFORM.dynamics.com/api/data/v9.2/"
url_add_on <- "?fetchXml="
xml <- "<fetch version=\"1.0\" output-format=\"xml-platform\" mapping=\"logical\" distinct=\"false\">
<entity name=\"amendments\">
<attribute name=\"seasonyear\" />
<attribute name=\"enrollmenttype\" />
<attribute name=\"effectivestartdate\" />
<attribute name=\"effectiveenddate\" />
<attribute name=\"contractstartdate\" />
<attribute name=\"contractenddate\" />
<attribute name=\"program\" />
<attribute name=\"programduration\" />
<attribute name=\"programtype\" />
<attribute name=\"kwnominations\" />
<attribute name=\"renewalkw\" />
<attribute name=\"newkw\" />
<attribute name=\"account\" />
<attribute name=\"ownerid\" />
<attribute name=\"amendmentsid\" />
<attribute name=\"statuscode\" />
<attribute name=\"statecode\" />
<attribute name=\"amendment\" />
<attribute name=\"name\" />
<order attribute=\"amendmentsid\" descending=\"false\" />
<link-entity name=\"salesorder\" from=\"salesorderid\" to=\"order\" visible=\"false\" link-type=\"outer\" alias=\"sales\">
<attribute name=\"datecontractapproved\" />
</link-entity>
<link-entity name=\"account\" from=\"accountid\" to=\"account\" visible=\"false\" link-type=\"outer\" alias=\"account\">
<attribute name=\"accountmanager\" />
<attribute name=\"accountnumber\" />
<attribute name=\"statecode\" />
</link-entity>
</entity>
</fetch>"
url_xml <- URLencode(xml)
dynamics_entity <- "amendmentses"
url_fetch <- paste0(base_url, dynamics_entity, url_add_on, url_xml)
resp <- GET(url = url_fetch
, config(token = mytoken)
, add_headers(Prefer = "odata.include-annotations=\"*\"")
)
if(http_error(resp) == TRUE){
print("Authentication error, unable to proceed.")
} else {
resp_json <- rawToChar(resp[["content"]])
resp_list <- fromJSON(resp_json)
df_data_raw <- resp_list[["value"]]
paging_cookie_resp <- resp_list[["@Microsoft.Dynamics.CRM.fetchxmlpagingcookie"]]
if(length(paging_cookie_resp) == 0){
print("No paging cookie returned, only one page of results.")
} else {
print("Retrieving multiple pages of results.")
page_number <- 1
last_page_found <- FALSE
while(last_page_found == FALSE){
lst_paging_cookie_resp <- str_split(paging_cookie_resp,"\"")
encoded_paging_cookie <- lst_paging_cookie_resp[[1]][4]
decoded_paging_cookie <- URLdecode(URLdecode(encoded_paging_cookie))
lst_decoded_paging_data <- str_split(decoded_paging_cookie,"\"")
decoded_paging_cookie <- str_remove(decoded_paging_cookie,"^\"")
decoded_paging_cookie <- str_remove(decoded_paging_cookie,"\"$")
# Replace any special characters with their HTML equivalents
decoded_paging_cookie <- str_replace_all(decoded_paging_cookie,"&","&")
decoded_paging_cookie <- str_replace_all(decoded_paging_cookie,"<","<")
decoded_paging_cookie <- str_replace_all(decoded_paging_cookie,">",">")
decoded_paging_cookie <- str_replace_all(decoded_paging_cookie,"\"",""")
URI_encoded_paging_cookie <- encodeURIComponent(decoded_paging_cookie)
page_number = page_number + 1
xml_header <- "paging-cookie=\"PutPagingCookieHere\" page=\"PutPageNumberHere\" distinct="
url_encoded_xml_header <- URLencode(xml_header)
url_encoded_xml_header <- url_encoded_xml_header %>%
str_replace("PutPagingCookieHere",URI_encoded_paging_cookie) %>%
str_replace("PutPageNumberHere",as.character(page_number))
new_url_xml <- str_replace(url_xml,"distinct=",url_encoded_xml_header)
url_fetch <- paste0(base_url, dynamics_entity, url_add_on, new_url_xml)
resp <- GET(url = url_fetch
, config(token = mytoken)
, add_headers(Prefer = "odata.include-annotations=\"*\"")
)
if(http_error(resp) == TRUE){
print("Error while retrieving 2nd page of results, unable to proceed.")
last_page_found <- TRUE
} else {
resp_json <- rawToChar(resp[["content"]])
resp_list <- fromJSON(resp_json)
df_data_raw_next_page <- resp_list[["value"]]
prev_page_names <- names(df_data_raw)
missing <- setdiff(prev_page_names, names(df_data_raw_next_page))
df_data_raw_next_page[missing] <- NA
next_page_names <- names(df_data_raw_next_page)
missing <- setdiff(next_page_names, names(df_data_raw))
df_data_raw[missing] <- NA
df_data_raw <- df_data_raw %>%
rbind(df_data_raw_next_page, use.names=TRUE) %>%
filter(`@odata.etag` != "TRUE") %>%
distinct()
paging_cookie_resp <- resp_list[["@Microsoft.Dynamics.CRM.fetchxmlpagingcookie"]]
if(nrow(df_data_raw_next_page) < 5000){
last_page_found <- TRUE
} else {
print(paste0("Page ",page_number," retrieved, retrieving page ", page_number + 1))
}
}
}
}
i <- 1
while(i <= length(names(df_data_raw))){
str_col_name <-names(df_data_raw)[i]
if(is.na(str_col_name) == FALSE){
condition_1 <- grepl("@Microsoft.Dynamics.CRM",str_col_name, ignore.case = TRUE)
condition_2 <- grepl("Display.V1.AttributeName",str_col_name, ignore.case = TRUE)
condition_3 <- grepl("@odata_etag",str_col_name, ignore.case = TRUE)
condition_4 <- grepl("@odata.etag",str_col_name, ignore.case = TRUE)
if(condition_1 | condition_2 | condition_3 | condition_4){
df_data_raw <- df_data_raw %>%
select(-all_of(str_col_name))
i <- 0
} else {
if(grepl("@OData.Community.Display.V1.FormattedValue",str_col_name, ignore.case = TRUE)){
str_base_col <- str_replace(str_col_name,"@OData.Community.Display.V1.FormattedValue","")
df_data_raw <- df_data_raw %>%
select(-all_of(str_base_col))
i <- 0
} else {
str_base_col <- str_col_name
}
str_new_col <- str_replace(str_base_col,"_","")
str_new_col <- str_replace(str_new_col,"\\.","_")
str_new_col <- str_replace(str_new_col,"_value$","") # Remove "_value" suffix
# Re-name the old column name to the new one
df_data_raw <- df_data_raw %>%
rename(!!str_new_col := all_of(str_col_name))
# If this is a character column that has the word "date" in it attempt to convert it to the
# a date-type column.
if(grepl("date",str_new_col,ignore.case = TRUE) & typeof(df_data_raw[,i]) == "character"){
print(paste0("Attempting to convert ",str_new_col," to a date format."))
# Attempt to assign the proper data type
df_data_raw <- df_data_raw %>%
mutate(attempt_mdy = mdy(!!as.symbol(str_new_col)))
# If we had at least 1 successful conversion, convert the
# column to the date format
if(sum(is.na(df_data_raw$attempt_mdy) == FALSE) > 0){
df_data_raw <- df_data_raw %>%
mutate(!!as.symbol(str_new_col) := attempt_mdy)
}
# Drop the attempt_mdy column
df_data_raw <- df_data_raw %>%
select(-attempt_mdy)
}
}
}
# Move up the index to the next column
i <- i + 1
}
# Select desired columns
df_data <- df_data_raw %>%
select(contract_account_number = account_accountnumber
, program_duration = programduration
, contract_start_date = contractstartdate
, contract_end_date = contractenddate
, season_year = seasonyear
, state_code = statecode
, status_code = statuscode
, account
, account_state_code = account_statecode
, program
, name
, kw_new = newkw
, kw_renewal = renewalkw
, kw_nomination = kwnominations
, account_manager = account_accountmanager
, contract_approval_date = sales_datecontractapproved
, enrollment_type = enrollmenttype
, effective_start_date = effectivestartdate
, effective_end_date = effectiveenddate
, owner = ownerid
, program_type = programtype
, amendment
, amendment_id = amendmentsid
)
# Set numeric data types
df_data <- df_data %>%
# Remove commas
mutate(season_year = gsub(",","",season_year)
, kw_new = gsub(",","",kw_new)
, kw_renewal = gsub(",","",kw_renewal)
, kw_nomination = gsub(",","",kw_nomination)) %>%
# Convert to numeric values
mutate(season_year = as.numeric(season_year)
, kw_new = as.numeric(kw_new)
, kw_renewal = as.numeric(kw_renewal)
, kw_nomination = as.numeric(kw_nomination))
# Add a load_date_time column
df_data$load_date_time <- Sys.time()
}