Functional Weave
Code in Python

todo.export-csv@1.0.0

README.md

2,814 bytes · view raw

# todo.export-csv

Writes a list of todos as CSV text: a header row and one row per todo, in the
order given. Use it for a "download my todos" button, a backup, or handing the
list to a spreadsheet or another app. todo.import-csv reads the same text
back.

## The file format

CSV as in [RFC 4180, Common Format and MIME Type for Comma-Separated Values
(CSV) Files](https://www.rfc-editor.org/rfc/rfc4180). The same format is
written by todo.export-csv and read by todo.import-csv.

- A header row, then one row per todo. todo.export-csv writes these 13 columns
  in this order:
  `id,title,notes,done,priority,due,tags,recurrenceFrequency,recurrenceInterval,recurrenceAnchor,createdAt,completedAt,order`
- `done` is `true` or `false`. `tags` are joined with single spaces. The
  repeat takes three columns, all empty when the todo does not repeat.
  `order` and `recurrenceInterval` are decimal integers. A null field
  (`notes`, `due`, `completedAt`) is empty.
- A field is quoted with double quotes exactly when it contains a comma, a
  double quote, CR or LF, and a double quote inside it is doubled. Nothing
  else is quoted, and spaces are kept as they are.
- Every row, the last one included, ends with CRLF.

**Caveat for spreadsheets.** Nothing is escaped against formula injection: a
title such as `=1+1` or `=HYPERLINK(...)` is written as it is, because
prefixing it with `'` would change the title when the file is read back. If
your app offers the file for opening in Excel, LibreOffice or Google Sheets,
warn people, or write a separate spreadsheet export that escapes cells
starting with `=`, `+`, `-` or `@`.

## Why it is shaped this way

One column per field, with the repeat split into three plain columns rather
than packed into one, so the file is readable in a spreadsheet and easy for
other tools to produce. Columns are named after the `Todo` fields, so the
header documents itself.

CRLF line endings and minimal quoting are what RFC 4180 describes and what
spreadsheets expect; quoting only when needed keeps the file diff-friendly.

## Edge cases

- No todos: just the header line, still ending in CRLF.
- Notes that are `""` are written as an empty field, the same as null
  notes, so they read back as null. Every other valid list reads back exactly:
  `importCsv(exportCsv(todos))` equals `todos`.
- Line breaks inside notes (LF, CRLF or a lone CR) are kept inside quotes as
  they are.
- It does not validate and never throws: a todo is written as it is, even an
  invalid one (a negative order is written `-1`). Validate on the way in, or
  let todo.import-csv refuse the file.
- The text is a string; encode it as UTF-8 when saving. No byte order mark is
  written (some versions of Excel need one to read UTF-8; add `\uFEFF` in
  front if yours does, todo.import-csv accepts it).