planedrop/README.md
2026-03-02 15:41:59 +03:00

6.0 KiB
Raw Permalink Blame History

notion2plank

One-shot Python script that imports tasks from a Notion CSV export into a self-hosted Plane instance via its REST API.

Plane's free/community tier has no built-in import UI, but the API is fully open. This script bridges the gap.


Requirements

  • Python 3.10+
  • A Plane API key — Settings → API Tokens
  • A Notion database exported as CSV — ··· → Export → Markdown & CSV → without subpages
pip -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Setup

cp config.yaml.example config.yaml

Edit config.yaml:

plane_url: https://your-plane-instance.com
workspace_slug: your-workspace
project_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
api_key: your-api-key-here

# Optional — needed to import estimate points (вес column).
# Get it from browser DevTools → Application → Cookies → session-id.
# If omitted, the вес column is silently skipped.
plane_session: your-session-id-cookie-value

# Map Notion status values (exact text + emoji) → Plane state names.
# Find your state names: GET /api/v1/workspaces/<slug>/projects/<id>/states/
status_mapping:
  "в работе 🔨": "In Progress"
  "Готово ✅": "Done"
  "На проверке 👀": "Review"
  "Утверждённые задачи 📝": "Todo"
  "Общие идеи (задачи) 📋": "Backlog"

Usage

Dry run first — parses every row and shows what would be created, no API calls:

python import.py --dry-run

Real import:

python import.py

Custom paths:

python import.py --csv path/to/export.csv --config path/to/config.yaml

Output

Fetching project states…
  Found 6 states: ['Backlog', 'Todo', 'In Progress', 'Done', 'Cancelled', 'Review']
Fetching project labels…
  Found 7 labels: ['Sound', '2d', 'Game Design', 'Org', '3d', 'Level Design', 'Devel']
Fetching estimate points…
  Found 6 estimate points: ['1', '2', '3', '5', '8', '13']

Ensuring labels for disciplines: ['Game Design', 'Sound', ...]

Processing 37 rows…

  [  1] Created: #31 — 'Create Kanban Board'
  [  2] Created: #32 — 'Add Milestones'
  ...

Summary: 37 created, 0 failed.

Field Mapping

Notion column Plane field Notes
Name name Required
Status state (UUID) Mapped via status_mapping in config
priority priority Emoji stripped: 🔥 Critical→urgent, High→high, 📌 Medium→medium, 💤 Low→low, empty→none
discipline label_ids Auto-created as a Plane label if it doesn't exist yet
Date (single) target_date Parsed with dateutil; output YYYY-MM-DD
Date (range A → B) start_date + target_date Split on the Unicode arrow
Description description_html Wrapped in <p>
результат appended to description_html Added as <p><em>Result: N</em></p> if non-zero
вес estimate_point (UUID) Matched by value (e.g. "5") then by key position; requires plane_session
исполнитель Skipped (no user ID mapping)
Автор задачи Skipped
Attachments Skipped
Person Skipped

Notes

Estimate points (вес)

Plane's public API (/api/v1/) does not expose an endpoint to list estimate point UUIDs. The script works around this by calling an internal frontend endpoint (/api/workspaces/…/estimates/) using your browser session cookie.

Resolution order:

  1. Exact value matchвес="5" maps to the point labeled "5" (Fibonacci-style: 1, 2, 3, 5, 8, 13)
  2. Ordinal key fallbackвес="4" maps to the 4th point in the estimate scale

If plane_session is not set or the cookie has expired, the вес column is skipped and everything else still imports normally.

Custom fields (work item properties)

Plane's custom property API (/work-item-types/…/work-item-properties/) requires the is_issue_type_enabled project flag, which is a paid-tier feature. It is not available in the community/self-hosted free edition.

Discipline labels

All unique discipline values in the CSV are pre-fetched and compared against existing project labels before the import starts. Missing labels are created automatically via POST /labels/. Existing labels are reused by UUID.


Files

import.py            — main script
config.yaml.example  — annotated config template
config.yaml          — your local config (do not commit)
requirements.txt     — pip dependencies