'Initialise output location outside of rmarkdown::render

Mikko Marttila helped me to find a way to dynamically timestamp files, using the working code below:

---
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)
})
---

I would however additionally like to specify the output location from within the knitr code, based on the projectmanager and project. The folder should be created if it does not yet exist.

This could be done as follows: link. I have tried to combine these solutions.

I have tried to do this as in a couple of different ways, but somehow I can't get it to work.

Desired output location:

.../projectname/project")

I tried using setwd as well as using file.path :

---
title: "Untitled"
author: "Jane Doe"
date: "`r Sys.Date()`"
output: word_document
projectmanager: "Earth Worm Jim"
project: "getstuffdone"
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))
    # Tom: Specify the main directory in which to store all folders
    main_directory <- "Z:/Coding/R_scripts/Markdown/RMarkdown_out/"
    # Check current working directory
    print(getwd())
    # print(metadata)

    # Dynamically create a full path for the new file based on projectmanager and project
    out_dir <- with(metadata, paste0(main_directory, projectmanager, "/", project, "/"))
    print(out_dir)

    # Here I tried to set the working directory to the dynamically created wd
    setwd(out_dir)
    print(getwd())

    # Build output file name based on rendered metadata
    output_name <- with(metadata, paste(title, date, initials))
    print(output_name)
    output_dir = file.path(dirname(output_name), out_dir)
    print(output_dir)

    # path_ext() returns the last extension (if any) for a path
    output_ext <- fs::path_ext(output_file_temp) 
    print(output_ext)
    
    # replaces the extension with a new extension path_ext_set(path, ext)
    output_file <- fs::path_ext_set(output_name, output_ext) 
    print(output_file)

    fs::file_move(output_file_temp, output_file)
    message("Output file: ", output_file)
  })
---


Solution 1:[1]

You're on the right track, but you need to also add a step to create the output directory if needed, and remember to use the created directory when moving the rendered file.

I would steer clear from changing the working directory in a script. Working with the full paths directly tends to lead to easier to understand code.

Here's a modified version with the missing steps added:

---
title: "Untitled"
author: "Jane Doe"
date: "`r Sys.Date()`"
output: word_document
projectmanager: "Earth Worm Jim"
project: "getstuffdone"
knit: >
  (function(input_file, encoding) {
    # Render, keeping intermediate files for extracting front matter
    md_dir <- tempdir()
    rendered_file <- 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))
    
    # Directory that the output is relative to. Can be an absolute path.
    OUTPUT_ROOT <- "."
    
    # Build the output file name and directory based on rendered metadata
    output_dir <- with(metadata, fs::path(projectmanager, project))
    output_name <- with(metadata, paste(title, "by", author, "on", date))
    
    # Add the file extension
    output_ext <- fs::path_ext(rendered_file)
    output_file <- fs::path_ext_set(output_name, output_ext)
    
    # Combine parts to final path
    output_path <- fs::path(OUTPUT_ROOT, output_dir, output_file)
    
    # Create output directory if it doesn't exist
    output_path_dir <- fs::path_dir(output_path)
    if (!fs::dir_exists(output_path_dir)) {
      fs::dir_create(output_path_dir)
    }
    
    # Move render result to output location
    fs::file_move(rendered_file, output_path)
    message("Output moved to: ", output_path)
  })
---

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mikko Marttila