R/backend.R

### ----------------------------------------------------------------- ###
### FILE ----
### ----------------------------------------------------------------- ###

# APIS ----

#' Write note into disk
#' @noRd
writeNoteIntoDisk <- function(note, file, type = "json") {
  stopifnot(isNote(note))
  writeLines(note, file)
  invisible(NULL)
}

#' Read note from notes folder
#' 
#' Does not read malformed note files.
#' 
#' @param note file name or hash (as relative).
#' @details
#' Input argument can either be provided as a json path or just the name of path
#' without json extension.
#' @importFrom jsonlite fromJSON
#' @export
readNote <- function(note) {
  stopifnot(is.character(note))
  stopifnot(length(note) == 1L)
  
  if (!grepl("\\.json$", note)) {
    note.sans.ext <- note
    note.avec.ext <- paste(note, ".json", sep = "")  
  } else {
    note.sans.ext <- tools::file_path_sans_ext(note)
    note.avec.ext <- note
  }
  
  if (!is_note_exist(note.avec.ext)) {
    event_message(
      paste(
        "Note cannot be found!",
        "This message appears if you deleted it from the system.",
        sep = "\n"
      )
    )
    return()
  }
  
  dir <- get_notes_folder_path()
  
  path <- file.path(dir, note.avec.ext)
  read.note <- readLines(path)
  
  if (!length(read.note) >= 1) {
    stop("cannot read empty note", call. = FALSE)
  }
  
  read.json.note <- jsonlite::fromJSON(read.note)
  
  ## validate note that has valid fields:
  valid <- names(read.json.note) %in% c("dateCreated", "title", "content", "isStarred", "isArchived")
  
  if (!all(valid)) {
    stop(
      paste(
        "cannot read note with malformed fields:",
        paste(paste0("\"", names(read.json.note)[!valid], "\""), collapse = ", ")
      ),
      call. = FALSE)
  }
  
  read.json.note
}

#' Write a new note
#' 
#' @param title Title of the note.
#' @param content Note content.
#' 
#' @export
new_note <- function(title, content) {
  
  dir <- get_notes_folder_path()
  if (!dir.exists(dir)) {
    stop(paste(
      "\nFolder to keep notes not exists.",
      "Please run `create_notes_folder()` first.",
      sep = "\n"
    ))
  }
  
  note <- create_note(title, content)
  
  file <- create_note_file()
  
  writeNoteIntoDisk(note, file)
  
  event_message("A new note created: ", file)
  
  invisible(NULL)
}

#' Create a note
create_note <- function(title, content) {
  
  ## input validation:
  inputs <- list(title, content)
  stopifnot(all(sapply(inputs, is.character)))
  stopifnot(all(sapply(inputs, length) == 1))
  
  details <- note_schema(
    title = title, 
    content = content,
    isStarred = FALSE,
    isArchived = FALSE
  )
  
  json.format <- jsonlite::toJSON(details, pretty = TRUE)

  structure(json.format, class = "Note")
}

#' Edit note
#' 
#' @param overwrite default is \code{FALSE}. By default, the original note is not
#'   edited. A new entry will be added to the note file. To edit chose this variable
#'   as \code{TRUE}.
# edit_note <- function()

#' Change note status as deleted
#' 
#' This is surely a soft delete. You need to delete the note from system if you really want to delete it.
# delete_note <- function()

#' Change note status as archived
# archive_note <- function()

#' Change note status as starred
# star_note

#' A generic solution to change note status in a field
# change_note_status <- function(note, status)

#' Creates a new note file in the notes folder
#' 
#' @return The note path.
#' @noRd
create_note_file <- function() {
  dir <- get_notes_folder_path()
  note.name <- create_note_name()
  pat <- file.path(dir, note.name)
  file.create(pat)
  pat
}

#' Create note name
#' 
#' It's not an UUID, and a new hash will be generated (in a recursive way) if
#' the hash code exists in the notes directory.
#' 
#' @noRd
create_note_name <- function() {
  rand.str <- random_string()
  filename <- paste(rand.str, ".json", sep = "")
  if (!filename_exists_in_folder(filename)) {
    filename
  } else {
    event_message("Filename exists! Creating new one.")
    create_note_name()
  }
}

#' Checks if a file exists in notes folder:
filename_exists_in_folder <- function(filename) {
  stopifnot(is.character(filename))
  folder.list <- list_folder_notes()
  if (filename %in% folder.list) {
    TRUE
  } else {
    FALSE
  }
}

### ----------------------------------------------------------------- ###
### SCHEMAS ----
### ----------------------------------------------------------------- ###

#' Note schema structure
note_schema <- function(title, content, isStarred, isArchived) {
  
  stopifnot(is.character(title))
  stopifnot(is.character(content))
  stopifnot(is.logical(isStarred))
  stopifnot(is.logical(isArchived))
  
  ret <- list(
    dateCreated = Timestamp(),
    title = title,
    content = content,
    isStarred = isStarred,
    isArchived = isArchived
  )
  
  ret
}

### ----------------------------------------------------------------- ###
### TYPE CHECKERS ----
### ----------------------------------------------------------------- ###

isNote <- function(x) {
  inherits(x, "Note")
}

### ----------------------------------------------------------------- ###
### FOLDER ----
### ----------------------------------------------------------------- ###

#' See all notes in data frame form
#' 
#' @importFrom jsonlite fromJSON
#' @noRd
notes_as_data_frame <- function() {
  dir <- get_notes_folder_path()
  list <- list_folder_notes()
  res <- do.call(rbind, lapply(seq_along(list), function(i) {
    note <- list[i]
    ## ignore note that cannot be read (instead of stopping execution):
    vec <- try(readNote(note))
    if (inherits(vec, "try-error")) {
      return(NULL)
    }
    df <- as.data.frame(vec)
    cbind(note, df)
  }))
  res
}

#' Does note exist in the notes folder?
#' @noRd
is_note_exist <- function(note) {
  stopifnot(is.character(note))
  stopifnot(length(note) == 1L)
  list <- list_folder_notes()
  mat <- match(note, list)
  if (is.na(mat)) {
    FALSE
  } else {
    TRUE
  }
}

#' List JSON files in notes folder
#' 
#' @importFrom tools file_path_sans_ext
#' @noRd
list_folder_notes <- function() {
  dir <- get_notes_folder_path()
  list <- list.files(dir, pattern = "\\.json$")
  ## exclude dots:
  list <- setdiff(list, c(".", ".."))
  list
}

#' Create a folder to keep note files (if folder not exists)
#' 
#' As default option indicates, the notes folder will be created in the current
#' directory.
#' 
#' @export
create_notes_folder <- function() {
  
  set_notes_folder_path()
  dir <- get_notes_folder_path()
  
  ## creating directory:
  if (!dir.exists(dir)) {
    dir.create(dir)
    event_message("Folder has been created ", dir)
  } else {
    event_message("Folder already exists in ", dir)
  }
  
}
strboul/shinyNotes documentation built on June 2, 2019, 10:56 p.m.