Repository navigation
Add CSV import/export feature with Formatting module #79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
89ebb90
5b74d38
8279d37
45d8bbe
a87f2eb
5629379
f56df29
629a282
87ccf40
d09fdb2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,90 @@ | ||||||
| # Importing and Exporting Meetings | ||||||
|
|
||||||
| Biscotti can export your meetings to a CSV file, and import meetings from one — so | ||||||
| you can get your data out, or bring meeting notes in from other apps (Granola, | ||||||
| Otter, Notion exports, or your own scripts). | ||||||
|
|
||||||
| Only meetings are covered: the title, date, summary, notes, and transcript. Audio | ||||||
| files, tags, people, and calendar data are not exported or imported. | ||||||
|
|
||||||
| Both features live in **Settings → Import/Export**. | ||||||
|
|
||||||
| ## The CSV file | ||||||
|
|
||||||
| The first row of the file is a header. Export writes exactly these columns, in | ||||||
| this order: | ||||||
|
|
||||||
| ``` | ||||||
| id,title,created,summary,notes,transcript | ||||||
| ``` | ||||||
|
|
||||||
| Import is lenient about the header: | ||||||
|
|
||||||
| - Column names are matched ignoring case and surrounding spaces. | ||||||
| - These aliases from other apps' exports are understood: `document_id` → `id`, | ||||||
| `document_title` → `title`, `document_created` → `created`. A file with only | ||||||
| the `document_*` names imports fine. | ||||||
| - Any other column is ignored — extra columns from another app's export don't | ||||||
| hurt. | ||||||
|
|
||||||
| `id` is each meeting's unique identifier. It can be a UUID, or any unique string | ||||||
| (another app's document ID works). Dates in the `created` column can be: | ||||||
|
|
||||||
| - ISO-8601 with fractional seconds: `2026-01-03T14:26:42.017Z` | ||||||
| - ISO-8601 without fractional seconds: `2026-01-03T14:26:42Z` (offsets like | ||||||
| `-05:00` are fine in both forms) | ||||||
| - A bare calendar date: `2026-01-03` (read as local midnight) | ||||||
| - A bare integer (epoch time): below 100000000000 it is read as seconds, | ||||||
| otherwise as milliseconds | ||||||
|
|
||||||
| Anything else makes that row invalid — it is skipped with a warning, and the | ||||||
| rest of the file still imports. | ||||||
|
|
||||||
| A file with only a header row (no data rows) is valid to import: nothing is | ||||||
| imported, and Biscotti reports "Imported 0 meetings." | ||||||
|
|
||||||
| ## The transcript format | ||||||
|
|
||||||
| The transcript column is plain text. Biscotti's own format marks each speaker | ||||||
| turn with a bracketed timestamp and the speaker's name: | ||||||
|
|
||||||
| ``` | ||||||
| [0:23] Steve | ||||||
| Let's get started. | ||||||
|
|
||||||
| [0:31] Priya | ||||||
| I pushed the fix this morning. | ||||||
| ``` | ||||||
|
|
||||||
| On import, any line that isn't a header line becomes part of the current | ||||||
| speaker's turn. A plain transcript with no headers at all imports as one | ||||||
| "Unknown Speaker" per line at 0:00 — you can rename speakers in the meeting | ||||||
| afterwards. The form `[0:23] Steve: hello there` (name and text on one line, as | ||||||
| some other apps write it) is also understood. | ||||||
|
|
||||||
| ## Duplicates | ||||||
|
|
||||||
| Import never updates or overwrites an existing meeting. A row whose `id` is | ||||||
| already in your library (or appears twice in the same file — the first | ||||||
| occurrence wins) is skipped with a warning. | ||||||
|
|
||||||
| ## Warnings and errors | ||||||
|
|
||||||
| - **Warnings** (you can continue or cancel): rows that were skipped because | ||||||
| they already exist, were missing a required value (`id`, `title`, or | ||||||
| `created`), had the same ID as an earlier row, or had a different number of | ||||||
| fields than the header; rows with no summary, notes, or transcript (these | ||||||
| still import); a column appearing alongside its alias. | ||||||
| - **Errors** (the import is blocked): the file can't be read, isn't UTF-8 text, | ||||||
| has no header row, is missing `id`, `title`, or `created` columns, isn't | ||||||
| valid CSV, or contains no meetings that can be imported. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Qualify the “no meetings” blocking error. The guide says that an import is blocked when it “contains no meetings that can be imported.” This also describes a valid header-only CSV. Proposed fix- or contains no meetings that can be imported.
+ or contains data rows but no meetings that can be imported.📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
|
|
||||||
| ## Export | ||||||
|
|
||||||
| Export writes **every** meeting, newest first, to a file named | ||||||
| `Biscotti_export_{timestamp}.csv` (for example | ||||||
| `Biscotti_export_2026-09-01-142642.csv`). You pick where to save it. | ||||||
|
|
||||||
| The `id` column always carries Biscotti's own UUID for each meeting — an | ||||||
| imported meeting's original (non-UUID) identifier is never re-exported. If you | ||||||
| round-trip a file through another tool, use Biscotti's UUIDs as the identity. | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add language identifiers to both fenced examples.
markdownlint-cli2reports MD040 for the fences at Lines 17 and 51. Usecsvfor the header example andtextfor the transcript example.Proposed fix
Also applies to: 51-51
🧰 Tools
🪛 markdownlint-cli2 (0.23.2)
[warning] 17-17: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Source: Linters/SAST tools