2

I found the function below here. It works great, but I would like to dynamically name the output file 'analysis.docx', using the title of the document, the author and the current date.

title: thetitle
author: myinititals
date: "`r Sys.Date()`"
knit: (function(inputFile, encoding) { 
          out_dir <- 'test';
          rmarkdown::render(inputFile,
                            encoding=encoding, 
                            output_file=file.path(dirname(inputFile), out_dir, 'analysis.docx')) })

How do I make 'analysis.docx' dynamic in this case?

I found some more information here, but not my desired answer.

Tom
  • 2,173
  • 1
  • 17
  • 44
  • Interesting question. Since the title and author can also include dynamic content via arbitrary R expressions, it seems to me you'd have to do some sort of partial render first to figure out what they resolve to, and then do a final `render()` when the information is available to name the output file. Or alternatively, somehow extract these during the render and just rename the output file after the fact. – Mikko Marttila Feb 18 '22 at 13:25

1 Answers1

7

If the fields that you want to use don't include R expressions, you can use yaml_front_matter() to extract their values and use those to construct the name for the output file:

---
title: "Untitled"
author: "Jane Doe"
date: "18/02/2022"
output: word_document
knit: >
  (function(input_file, encoding) {
    metadata <- rmarkdown::yaml_front_matter(input_file)
    output_file <- with(metadata, paste(title, "by", author))
    rmarkdown::render(input = input_file, output_file = output_file)
  })
---

204 No Content

If your fields do include R expressions, this becomes a little more involved. You can apply the same principle, but now instead of getting the front matter from the RMarkdown file, you get it from the intermediate Markdown file generated during the rendering process. Then rename the result.

That could look something like this:

---
title: "Untitled"
author: "Jane Doe"
date: "`r Sys.Date()`"
output: word_document
knit: >
  (function(input_file, encoding) {
    # Render, keeping intermediate files for extracting front matter
    md_dir <- tempdir()
    output_file_temp <- rmarkdown::render(
      input = input_file,
      output_file = tempfile(),
      intermediates_dir = md_dir,
      clean = FALSE
    )
    
    # Get the rendered front matter from the intermediate Markdown file
    md_file <- fs::path_ext_set(fs::path_file(input_file), ".knit.md")
    metadata <- rmarkdown::yaml_front_matter(fs::path(md_dir, md_file))
    
    # Build the output file name based on rendered metadata
    output_name <- with(metadata, paste(title, "by", author, "on", date))

    # Add the file extension and move to the working directory
    output_ext <- fs::path_ext(output_file_temp)
    output_file <- fs::path_ext_set(output_name, output_ext)
    fs::file_move(output_file_temp, output_file)

    message("Output moved to: ", output_file)
  })
---

204 No Content
Mikko Marttila
  • 10,972
  • 18
  • 31
  • It might be a nice feature in the rmarkdown package to allow `output_file` in `render()` to be a function that has access to the rendered metadata. – Mikko Marttila Feb 18 '22 at 14:51
  • Thank you very much! I'm getting the error `Error: unexpected ',' in "('C:/Users/user/OneDrive/path/Untitled.Rmd',"` I am having a hard time debugging the function. Do you have any idea what might be the cause? – Tom Feb 18 '22 at 14:59
  • That seems like a typo to me. Are you entering that path somewhere? It should probably be `"C:/Users/user/OneDrive/path/Untitled.Rmd"`. – Mikko Marttila Feb 18 '22 at 15:07
  • I'm not.. that is the point.. – Tom Feb 18 '22 at 15:10
  • I see, that's odd. What steps have you taken that lead to the error? – Mikko Marttila Feb 18 '22 at 15:16
  • As your code did not seem to include any special references, I simply replaced the old function I had with your function. I also tried your solution for when the fields do not include R expressions. I get exactly the same error, maybe that helps? – Tom Feb 18 '22 at 15:25
  • Did you include the `>` after `knit:` to make the function a multi-line string? Could you also try just copying the entire file and rendering that? I'm thinking there's probably something wrong with how the YAML is formatted. – Mikko Marttila Feb 18 '22 at 15:32
  • I did include the `>`, Just rendering your code by itself in a new file does run, however the file created is `Output created: C:\Users\user\AppData\Local\Temp\Rtmp6jjWck\file61ec765f3927.docx`. – Tom Feb 18 '22 at 15:38
  • Thanks for trying that! Yeah that message is the last one generated by `rmarkdown::render()`. It's then moved in the function to the result file. It should be in your working directory. Maybe it would be clearer to include a message for that (added it now). That said, I find it very strange that the function doesn't work with your actual file, then. Did you edit in the output directory or anything like that where there could have been a typo? – Mikko Marttila Feb 18 '22 at 15:41
  • Ah okay! I found the file created by your script, so the script works! Thank you very much. From this point I think I can figure it out by myself. I will just keep adding lines from my original script to the new one, until it breaks to figure out what makes it break. Once again thank you very much for your amazing function and enormous patience! – Tom Feb 18 '22 at 15:46
  • 1
    Okay great, glad to hear that! You're very welcome, this was an interesting problem. I also took a moment to file a feature request on rmarkdown GitHub, so if that goes through this might become easier in the future: https://github.com/rstudio/rmarkdown/issues/2314 – Mikko Marttila Feb 18 '22 at 15:48
  • For future reference, I found the reason for the error. Apparently you cannot specify anything related to the output after `knit: >`. – Tom Feb 18 '22 at 16:17
  • Oh that's strange! Thanks for sharing that. – Mikko Marttila Feb 18 '22 at 16:19
  • Related question: https://stackoverflow.com/questions/71751149/initialise-output-location-outside-of-rmarkdownrender – Tom May 02 '22 at 08:36
  • The best solution I have found so far. What if the title depends of the result of a variable that is create later in the code? I have tried this approach using the code here https://bookdown.org/yihui/rmarkdown-cookbook/dynamic-yaml.html and it's not working. Thanks. – Ernesto561 Jun 27 '22 at 18:33
  • 1
    @Ernesto561 I think you'd have to use a two-stage render, first rendering the R Markdown document to Markdown and capturing the variable that you want to use in the title. Then update the title in the Markdown document YAML front matter, and use Pandoc to convert the modified Markdown into the final output format. – Mikko Marttila Jul 13 '22 at 11:30
  • Thanks, sound like it requires some extra work. I'll check it out. – Ernesto561 Jul 14 '22 at 13:50