I'm trying (as hard as i can) to create a script that will generate formatted word documents from plain text files using R language and reporteRs.
To extract text from one txt i'm using this code found on this thread Dealing with readLines() function in R :
fileName <- "C:/MyFolder/TEXT_TO_BE_PROCESSED.txt"
con <- file(fileName,open="r")
line <- readLines(con)
close(con)
Then add the extracted text to docx with this :
doc <- docx(template="temp.docx")
Next, adding the title (first line of the txt file)
doc <- addParagraph( doc, value = line[1], bookmark = "titre", stylename = "Titre")
then the body of the txt file
doc <- addParagraph( doc, value = line[2:length(line)], value = line[2:55], stylename = "Contenu")
Finally I create the docx
writeDoc(doc, file = "output-file.docx")
I want to be able to create a loop so I can generate multiple docx from multiple txt files. I will really appreciate your help
You can do something like this with lapply
myFiles <- c("C:/MyFolder/TEXT_TO_BE_PROCESSED.txt", "C:/MyFolder/TEXT_TO_BE_PROCESSED2.txt") # or use list.files()
lapply(myFiles, function(fileName){
con <- file(fileName,open="r")
line <- readLines(con) # you could just call readLines(fileName)
close(con)
doc <- docx(template="temp.docx")
doc <- addParagraph( doc, value = line[1], bookmark = "titre", stylename = "Titre")
doc <- addParagraph( doc, value = line[2:length(line)], value = line[2:55], stylename = "Contenu")
writeDoc(doc, file = paste0(fileName, "out.docx"))
})
The solution :
myFiles <- list.files()
lapply(myFiles, function(fileName){
line <- readLines(fileName)
doc <- docx(template="temp.docx")
doc <- addParagraph( doc, value = line[1], bookmark = "titre",
stylename = "Titre ยป)
doc <- addParagraph( doc, value = line[2:length(line)], stylename = "Contenu")
writeDoc(doc, file = paste0(fileName, ".docx"))
})
Thank you again Richard
Related
I am working on a shiny app where the user will upload an excel file, the data will be manipulated, and then a new excel file with this data is exported for the user to examine. I am having issues with the downloadHandler function. I used to create an entirely new excel file every time based on the uploaded data like this:
output$export <- downloadHandler(
filename = "answers.xlsx",
content = function(file){
write.xlsx(exportdata(), file)
})
})
This works fine.
Now I would like to edit an excel file that I will include when I publish the app and allow the user to download this edited version like this:
output$export <- downloadHandler(
filename = "answers.xlsx",
content = function(file){
wb <- loadWorkbook("6rep-charts.xlsx")
writeData(wb, sheet = "Species Match Results", correlInput())
writeData(wb, sheet = "BS1 Data", bs1Input())
writeData(wb, sheet = "BS2 Data", bs2Input())
saveWorkbook(wb, file)
})
However, this results in the error Warning: Error in write_file: Expecting a single string value: [type=character; extent=0]. [No stack trace available]. I am not sure what is going wrong as when I run the content section outside of the shiny app, it works just fine. The problem seems to be in the saveWorkbook command.
The reason I would like to edit an existing excel file rather than create a new one is that the template file I'm including in the app has charts already made that should change when the new data is written into the file. The users would like to be able to edit these charts themselves, rather than just see a picture of a graph. If anyone has a simpler way to accomplish this, that would be great! Thank you in advance for your help!
Reproducible example using this excel file:
library(shiny); library(readxl); library(xlsx)
ui <- shinyUI(fluidPage(
titlePanel("Old Faithful Geyser Data"),
fluidRow(
column(3,
downloadButton(outputId = "export",
label = "Export Results to Excel")
),
column(6,
dataTableOutput("data")
))))
server <- function(input, output) {
adata <- faithful[1:20,]
bdata <- faithful[21:50,]
cdata <- faithful[51:200,]
read_excel_allsheets <- function(filename, tibble = FALSE) {
sheets <- readxl::excel_sheets(filename)
x <- lapply(sheets, function(Y) {readxl::read_excel(filename, sheet = Y)})
if(!tibble) x <- lapply(x, as.data.frame)
names(x) <- sheets
x
}
output$data <- renderDataTable({
adata })
output$export <- downloadHandler(
filename = "answers.xlsx",
content = function(file){
wb <- loadWorkbook("./Data/template.xlsx")
writeData(wb, sheet = "Alpha", adata)
writeData(wb, sheet = "Beta", bdata)
writeData(wb, sheet = "Gamma", cdata)
saveWorkbook(wb, file="./Data/temp.xlsx", overwrite = T)
print("done")
Fin_WB<- read_excel_allsheets("./Data/temp.xlsx")
write.xlsx(Fin_WB, file)
} ) }
shinyApp(ui = ui, server = server)
Simpler solution (I am unable to leave comments due to too low reputation):
output$export <- downloadHandler(
filename = "answers.xlsx",
content = function(file){
wb <- loadWorkbook("./Data/template.xlsx")
writeData(wb, sheet = "Alpha", adata)
writeData(wb, sheet = "Beta", bdata)
writeData(wb, sheet = "Gamma", cdata)
saveWorkbook(wb, file="./Data/temp.xlsx", overwrite = T)
file.copy(from = "./Data/temp.xlsx", to = file)
})
No need for the function read_excel_allsheets.
The saveWorkbook function does not like to operate as a write function for the content function. Confusing yes, but we can still use it within the content function, just not as the final step. We have to use a classic write function such as write.xlsx to satisfy the content function which is expecting the file and file path to write to.
To get around this we create a temp file in the Data folder (this folder will be in the dir with server and ui, and will be published). The program reads in the template file (see special function below), modifies it, and then writes/overwrites the temp file. This temp file is then read in and assigned by read.xlsx and then written to by write.xlsx. This satisfies the content function and allows us to use a template for export.
read_excel_allsheets <- function(filename, tibble = FALSE) {
sheets <- readxl::excel_sheets(filename)
x <- lapply(sheets, function(X) readxl::read_excel(filename, sheet = X))
if(!tibble) x <- lapply(x, as.data.frame)
names(x) <- sheets
x
}
output$export <- downloadHandler(
filename = "answers.xlsx",
content = function(file){
wb <- loadWorkbook("./Data/template.xlsx")
writeData(wb, sheet = "Alpha", adata)
writeData(wb, sheet = "Beta", bdata)
writeData(wb, sheet = "Gamma", cdata)
saveWorkbook(wb, file="./Data/temp.xlsx", overwrite = T)
print("done")
Fin_WB<- read_excel_allsheets("./Data/temp.xlsx")
write.xlsx(Fin_WB, file)
})
}
In order to read in all the sheets, we cannot simply use read.xlsx as it wont look at all the sheets. Using a function created here by Jeromy Anglim, we can read in all the sheets from excel. This is the read_excel_allsheets function.
I have a Shiny app that reads in a gpx track file and buffers it. I then want the user to be able to download that shapefile to a destination of their choice. I have been trying to use the downloadHandler function, but so far I have no joy.
The name of the shapefile that I have created is called trk_buff.
Within R I can just use:
my_dsn<-"C:\\Documents\\TrackFiles"
writeOGR(obj=trk_buff, dsn=my_dsn, layer="BufferedTracks", driver="ESRI Shapefile")
I have tried to use the downloadHandler thus:
output$downloadData<-downloadHandler(
filename=function(file){trk_buff},
content=function(file){
writeOGR(obj=trk_buff, dsn=file, layer="BufferedTracks", driver="ESRI Shapefile")
})
But I am obviously doing something wrong as nothing happens... :|
EDIT TO ADD
I can get the behaviour I want if I use a combination of an action Button and a textFile box.
But that is a little clumsy and involves the user explicitly writing the filepath rather than searching for it, which will probably lead to errors:
e.g.
in the ui.R I have:
textInput("filepath","Filepath to download data"),
actionButton("act1","Download shapefile")
In the server.R I have:
action_btn_code <- eventReactive(input$act1, {
file_path<-input$filepath
writeOGR(obj=trk_buff, dsn=paste(file_path,"/Tracks",sep=""), layer="BufferedTracks",
driver="ESRI Shapefile", overwrite_layer=TRUE)
})
The following works for me. The idea is that you have to zip up of the shapefiles because downloadHandler can only handle downloading one file.
output$download_shp <- downloadHandler(
filename = "shapefile.zip",
content = function(file) {
data = trk_buff() # I assume this is a reactive object
# create a temp folder for shp files
temp_shp <- tempdir()
# write shp files
writeOGR(data, temp_shp, "trk_buff", "ESRI Shapefile",
overwrite_layer = TRUE)
# zip all the shp files
zip_file <- file.path(temp_shp, "trk_buff_shp.zip")
shp_files <- list.files(temp_shp,
"trk_buff",
full.names = TRUE)
# the following zip method works for me in linux but substitute with whatever method working in your OS
zip_command <- paste("zip -j",
zip_file,
paste(shp_files, collapse = " "))
system(zip_command)
# copy the zip file to the file argument
file.copy(zip_file, file)
# remove all the files created
file.remove(zip_file, shp_files)
}
)
library(sf)
library(zip)
output$download_shp <- downloadHandler(
filename <- function() {
"Data_shpExport.zip"
},
content = function(file) {
withProgress(message = "Exporting Data", {
incProgress(0.5)
tmp.path <- dirname(file)
name.base <- file.path(tmp.path, "BufferedTracks")
name.glob <- paste0(name.base, ".*")
name.shp <- paste0(name.base, ".shp")
name.zip <- paste0(name.base, ".zip")
if (length(Sys.glob(name.glob)) > 0) file.remove(Sys.glob(name.glob))
sf::st_write(trk_buff, dsn = name.shp, ## layer = "shpExport",
driver = "ESRI Shapefile", quiet = TRUE)
zip::zipr(zipfile = name.zip, files = Sys.glob(name.glob))
req(file.copy(name.zip, file))
if (length(Sys.glob(name.glob)) > 0) file.remove(Sys.glob(name.glob))
incProgress(0.5)
})
}
)
I am writing up a report where the output gets pushed to a xlsx document via library(xlsx). This data then feeds into a table especially formatted with LaTeX code that formats the output:
```{r import_results, echo = F}
if(!file.exists("Analysis/results.xlsx")){
wb <- xlsx::createWorkbook(type = "xlsx")
sheets <- xlsx::createSheet(wb, "data")
}else{
wb <- loadWorkbook("Analysis/results.xlsx")
sheets <- removeSheet(wb, "data")
sheets <- xlsx::createSheet(wb, "data")
}
getSheets(wb)
addDataFrame(sheet = sheets, x = Results1)
addDataFrame(sheet = sheets, x = Results2, startRow = nrow(Results1)+2)
addDataFrame(sheet = sheets, x = Results3, startRow = nrow(Results1)+ nrow(Results2) + 4)
xlsx::saveWorkbook(wb, "Analysis/results.xlsx")
}
After writing to sheet that table data is linked to, I read it back into R, now with all the LaTeX in the cells and in essence I want to cat results so they are LaTeX code, but it prints the data.frame as a long string when I knit:
```{r, echo = F, results='asis'}
wb <- read.xlsx("Analysis/results.xlsx", sheetName = "import", header=F)
row.names(wb) <-NULL
wb
```
What is the appropriate way to automate this cross platform integration?
I got the example to work in a very "simplistic" manner. Although I dont think its the cleanest code out there, it does work for what I need it to do:
library(dplyr)
library(xlsx)
wb <- read.xlsx("Analysis/results.xlsx", sheetName = "import", header=F, stringsAsFactors = F)
wb <- mutate_each(wb, funs(replace(., which(is.na(.)), "")))
```{r, echo = F, results = 'asis'}
for(i in 1:nrow(wb)){
cat(unlist(wb[i,]),"\n")
}
```
I have a file which i generate in shiny
The user clicks a button and the file should download. However nothing happens
The function export_report generates the excel file and saves it to a location. The function then passes back the file location to the download handler so it will download the file. The problem seems to be that it isnt being returned correctly. I have tested the function (export_report) outside of shiny and it returns everything perfectly so I'm clearly doing something wrong from the shiny perspective.
The file itself is created where it is supposed to be on the server because i can download it within RStudio and see it in the file explorer. Can anyone help
# UI Section
downloadButton("downloadRpt", "Download Report")
# Server Section
output$downloadRpt <- downloadHandler(
filename = function() {
mydf <- report()
dateRange <- input$dates_report
selection <- input$selection
myfile <- export_report (mydf, selection, dateRange)
},
content = function(file) {
file.copy(myfile, file)
}
)
I have seen other examples R Shiny: Download existing file which is what my code is based on
EDIT 1: Adding the export_report function with some fake data to run it
export_report <- function(mydf,selection,dateRange) {
# Template for where the template excel file is stored
myoutputTemplate <- '/home/shiny_tutorials/Save to Database/templates/output_template.xlsx'
start_date <- dateRange[1]
end_date <- dateRange[2]
date_range <- paste(start_date ,end_date, sep = " - " )
# Load workbook the template workbook
wb <- loadWorkbook(myoutputTemplate)
# write to the workbook the data frame
writeWorksheet(wb, mydf, sheet="Details",
startRow=8, startCol=2,
header=FALSE)
# add the the customer the user selected
writeWorksheet(wb, selection, sheet="Details",
startRow=3, startCol=3,
header=FALSE)
# date
writeWorksheet(wb, date_range, sheet="Details",
startRow=5, startCol=3,
header=FALSE)
# Create The file Name
filename <- paste(selection, Sys.Date(), sep = " - ") %>%
paste(.,"xlsx", sep = ".")
# removes the % sign and extra qoutes
filename <- gsub (pattern = '\'|%','', x = filename)
# output directory
myoutput <- paste('/home/shiny_tutorials/Save to Database/output/',
filename, sep = '')
# Save workbook
saveWorkbook(wb, myoutput)
# Return File Path
myoutput
}
To call the function you can use the data below
dateRange <- c("2011-09-23","2016-09-23")
selection = "COMPANY_A"
mydf <- iris
myfile <- export_report(mydf,selection,dateRange)
EDIT 2 I have now managed to get an error out of it. When i cat(myfile) in the filename = function() { section of the code i get the error after the correct file path has been returned
Warning in rep(yes, length.out = length(ans)) :
'x' is NULL so the result will be NULL
Warning: Error in ifelse: replacement has length zero
Stack trace (innermost first):
1: runApp
Error : replacement has length zero
This error is basically because my file path does not get passed to the segment myfile so
if someone can tell me how to get the filepath generated by my function to the server section of the code below, that should fix my problem
content = function(file) {
file.copy(myfile, file)
}
Thank you to everyone who commented and clarified my thinking a bit on how the download handler works.
In the end, i created a new function which split up the export function above
The new function i used is called generate_file() which simply returns the file name
generate_file_name <- function(selection) {
# Create The file Name
filename <- paste(selection, Sys.Date(), sep = " - ") %>%
paste(.,"xlsx", sep = ".")
# removes the % sign and extra qoutes
filename <- gsub (pattern = '\'|%','', x = filename)
# output directory
myoutput <- paste('/home/shiny_tutorials/Save to Database/output/',
filename, sep = '')
# Return File Path
myoutput
}
Then in the server side
output$downloadRpt <- downloadHandler(
filename = function() {
selection <- input$company
generate_file_name(selection)
},
content = function(file) {
mydf <- report()
dateRange <- input$dates_report
selection <- input$company
export_report(mydf,selection,dateRange)
myfile <- generate_file_name(selection)
file.copy(myfile, file)
}
)
This then finds the newly created file and exports it for the user
I just checked your problem with this example code and it worked:
output$downloadData <- downloadHandler(
filename = function() {
data <- mtcars
myfile <- "data.csv"
write.csv(data, myfile)
myfile
},
content = function(file) {
print(file) //prints: [...]\\Local\\Temp\\RtmpEBYDXT\\fileab8c003878.csv
file.copy(file, file)
}
)
myfile is the filename of the downloaded file. You cannot use it in file.copy as input, this variable is out of scope. It seems that R creates a temp file name (see the print()).
Try to use the filename function to define your path or a custom file name, and the write.csv in the content part. Example code:
output$downloadData <- downloadHandler(
filename = function() {
paste(<user_name or date for eg>, ".csv", sep="")
},
content = function(file) {
write.csv(data, file)
}
)
I noticed in your comment above, you have asked how the application would generate the correct file when used by multiple users. For this part, you need to use the session.
So if your business logic functions were to come from an R file called foo.R, the server code should look something like:
shinyServer(func = function(input, output, session) {
source("./foo.R", local=TRUE)
......
And this would separate out the session for each user, thereby generating files specific to each, when downloading. Hope this gives you some pointers.
Using reportRs pacakge, I'm trying to add several graphs(.png/.jpg) which are named as e.g. test-0,test-1,test-2 etc into a pptx file. These graphs have been extracted from a pdf named e.g. test using im.convert function.I can add them individually but not able to automate the code for graphs,title, slide number, date etc in loop which can figure out how many graphs with 'test' name are there in a folder and then import them in the pptx one by one in a new slide ata time and one final pptx file.
sample code:
library(animation)
im.convert("Test.pdf", output = "Test.png", extra.opts="-density 150")
library("ReporteRs")
doc <- pptx()
doc <- pptx(template = templateDir)
doc <- addSlide( doc, slide.layout = 'Competative Landscape' )
doc <- addTitle(doc, paste("Test-0"))
doc <- addImage(doc, "Test-0.png")
:
:
:
:
doc <- addSlide( doc, slide.layout = 'Competative Landscape' )
doc <- addTitle(doc, paste("Test-3"))`enter code here`
doc <- addImage(doc, "Test-3.png")
You could try using the list.files function to find the number of png files with the name Test in a folder.
sample code:
list_of_files=list.files(path = "C:/output_folder", pattern = c("Test",".png"))
library("ReporteRs")
doc <- pptx()
doc <- pptx(template = templateDir)
for( i in 0:(length(list_of_files)-1))
{
doc <- addSlide( doc, slide.layout = 'Competative Landscape' )
doc <- addTitle(doc, paste0("Test-",i))
doc <- addImage(doc, paste0("Test-",i,".png"))
}
You could also try the eoffice package:
install.package("eoffice")
fig<-infigure("figes",savegg=T)
topptx(fig,file="test.pptx")
##or
infigure("figs",showfig=T)
topptx(fig,file="test.pptx")