---
title: "An Overview of readODS"
output: rmarkdown::html_vignette
author:
  - Chung-hong Chan ^[GESIS Leibniz-Institut für Sozialwissenschaften]
vignette: >
  %\VignetteIndexEntry{An Overview of readODS}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

```{r setup}
library(readODS)
```

You probably only need to use two functions from this package: `read_ods` and `write_ods`.

Write the data PlantGrowth (from the built-in `datasets` package) as a new file `plant.ods` in the current working directory of the user's session.

```{r write_ods1}
write_ods(PlantGrowth, "plant.ods")
```

You can then read it back from `plant.ods`

```{r read_ods1}
read_ods("plant.ods")
```

## Update and Append

You can append another sheet into an existing ods file with the sheet name being "mtcars_ods".

```{r write_ods_append}
write_ods(mtcars, "plant.ods", sheet = "mtcars_ods", append = TRUE)
```

Read from a specific sheet. Notice row names are missing.

```{r read_ods_mtcars}
read_ods("plant.ods", sheet = "mtcars_ods")
```

You can also integer for `sheet`, e.g. 2 for the second sheet.

```{r read_ods_mtcars2}
read_ods("plant.ods", sheet = 2)
```

Update an existing sheet and preserve row names

```{r write_ods_update}
write_ods(mtcars, "plant.ods", sheet = "mtcars_ods", update = TRUE, row_names = TRUE)
```

Notice the information from the sheet `mtcars_ods` is updated.

```{r read_ods_mtcars3}
read_ods("plant.ods", sheet = "mtcars_ods")
```

Read from a specific range

```{r read_ods_mtcars_range}
read_ods("plant.ods", sheet = "mtcars_ods", range = "A1:C10")
```

You cannot append to an existing sheet.

```{r append_error, error = TRUE, purl = FALSE}
write_ods(iris, "plant.ods", sheet = "mtcars_ods", append = TRUE)
```

You cannot update a missing sheet.

```{r update_error, error = TRUE, purl = FALSE}
write_ods(iris, "plant.ods", sheet = "iris", update = TRUE)
```

## Writing multiple sheets simultaneously

It is much faster to write data frames into the same file by putting them in a (named) list.

```{r writelist}
write_ods(list("iris" = iris, "plant" = PlantGrowth), "plant_multi.ods")
read_ods("plant_multi.ods", sheet = "plant")
```

## Flat ODS files (`.xml` or `.fods`)

Can be read with `read_ods()` [^1] (note that the same function is used to read flat files, no matter the extension).
This has the same behaviour and arguments as `read_ods()`

```{r read fods, eval = file.exists("plant.fods")}
read_fods("plant.fods")
```

`write_ods()` can be used to write Flat ODS files

```{r write_fods}
write_ods(PlantGrowth, "plant.fods")
```

## Misc.

Use the function `list_ods_sheets()` to list out all sheets in an (F)ODS file.

```{r, list_ods_sheets}
list_ods_sheets("plant.ods")
```

## readODS 2.0.0

Starting from 2.0.0, `write_ods` writes `NA` as empty by default.

```{r, empty1}
PlantGrowth2 <- tibble::as_tibble(PlantGrowth)
PlantGrowth2[1,1] <- NA
PlantGrowth2$group <- as.character(PlantGrowth2$group)

## NA is preseved; weight is still <dbl>
read_ods(write_ods(PlantGrowth2))
```

If you want `NA` to be written literally as the string "NA", use `na_as_string`. You should literally see the string "NA" when the file is opened with LibreOffice, for example.

But the string "NA" messes up the automatic type inference of `read_ods`.

```{r, empty2}
## NA is preseved; but weight is now <chr>
read_ods(write_ods(PlantGrowth2, na_as_string = TRUE))
```

Of course you can fix this by specifying `col_types`.

```{r, empty3}
## NA is preseved; but weight is now <chr>
read_ods(write_ods(PlantGrowth2, na_as_string = TRUE),
         col_types = readr::cols(weight = readr::col_double()))
```

Several functions were removed in readODS 2.0.0. Please consider the API of `readODS` mature and there should not be any breaking change until readODS 3.0.0.

### `ods_sheets`

Please use `list_ods_sheets(path = "plant.ods")` instead.

```{r, list_ods_sheets20}
## ods_sheets("plant.ods")
list_ods_sheets("plant.ods")
```

### `get_num_sheets_in_ods` and `getNrOfSheetsInODS`

Please use `list_ods_sheets`

```{r, getnum20}
##get_num_sheets_in_ods("plant.ods")
length(list_ods_sheets("plant.ods"))
```

### `read.ods`

Please use `read_ods`. In order to emulate the behaviours of `read.ods`, the followings are recommended

```{r, readdotods17}
## read.ods from 1.6 to 1.8
read_ods("plant.ods", col_names = FALSE, skip = 0, na = NULL, col_types = NA, as_tibble = FALSE)
```

```{r, readotods16}
## read.ods older than 1.6
lapply(list_ods_sheets("plant.ods"),
       function(x) read_ods(path = "plant.ods", sheet = x, col_names = FALSE, skip = 0, na = NULL, col_types = NA, as_tibble = FALSE))
```

```{r, echo = FALSE, message = FALSE}
unlink("plant.ods")
unlink("plant.fods")
unlink("plant_multi.ods")
```

---

[^1]: `read_fods()` and `list_fods_sheets()` are also available. But since version 2.2.0 `read_ods()` and `list_ods_sheets()` can determine whether the file at the `path` argument is flat or not.