← Files JinkōARCHIVED FILE

skills/jinko-protocol/scripts/create_protocol_design_from_csv.py

4.29 KB · Oct 2, 2026 · 00:29 UTC

↓ Download file

#!/usr/bin/env python3
"""Create a Jinkō protocol design from CSV-defined scenario arms.

The CSV is posted as-is; the platform parses and validates it against its
own protocol design CSV schema.

Dry-run by default. Pass --apply to create the ProtocolDesign project item.
"""

from __future__ import annotations

import argparse
import csv
import sys
from pathlib import Path

try:
    from dotenv import load_dotenv
except ImportError:  # pragma: no cover - depends on local environment
    load_dotenv = None


def load_env() -> None:
    if load_dotenv is not None:
        load_dotenv()


def load_sdk():
    try:
        from jinko import JinkoClient
        from jinko.exceptions import JinkoError
    except ImportError:
        print(
            "Cannot import jinko. Install the SDK: pip install jinko-sdk",
            file=sys.stderr,
        )
        return None
    return JinkoClient, JinkoError


def read_csv_summary(path: Path) -> tuple[list[str], int]:
    with path.open("r", encoding="utf-8", newline="") as handle:
        reader = csv.reader(handle)
        try:
            header = next(reader)
        except StopIteration:
            raise ValueError("CSV is empty") from None
        row_count = sum(1 for _ in reader)
    if row_count == 0:
        raise ValueError("CSV must contain at least one arm row")
    return header, row_count


def resolve_folder(client, folder_ref: str | None, *, create: bool):
    if folder_ref is None:
        return None

    folder = client.get_folder(folder_ref)
    if folder is not None:
        return folder

    folder = client.get_folder_by_name(folder_ref, exact_match_only=True)
    if folder is not None:
        return folder

    if not create:
        raise ValueError(
            f"Folder {folder_ref!r} was not found. Pass --create-folder to create it."
        )

    return client.create_folder(folder_ref)


def main() -> int:
    parser = argparse.ArgumentParser(
        description="Create a Jinkō protocol design from CSV-defined arms."
    )
    parser.add_argument(
        "--csv",
        required=True,
        help="CSV path matching the platform's protocol design CSV schema.",
    )
    parser.add_argument("--name", default="sdk-csv-protocol")
    parser.add_argument(
        "--description", default="Protocol design created from SDK CSV upload."
    )
    parser.add_argument(
        "--folder",
        help="Existing folder id or exact folder name for the new protocol design.",
    )
    parser.add_argument(
        "--create-folder",
        action="store_true",
        help="Create --folder when missing. Treats --folder as a folder name.",
    )
    parser.add_argument(
        "--apply", action="store_true", help="Actually create the protocol design."
    )
    args = parser.parse_args()

    if args.create_folder and not args.folder:
        print("--create-folder requires --folder", file=sys.stderr)
        return 1

    csv_path = Path(args.csv)
    try:
        header, row_count = read_csv_summary(csv_path)
    except ValueError as exc:
        print(str(exc), file=sys.stderr)
        return 1

    print(f"CSV columns: {', '.join(header)}")
    print(f"CSV arms: {row_count}")

    if not args.apply:
        print(f"Would create protocol design named {args.name!r}.")
        if args.folder:
            action = "Would create or reuse" if args.create_folder else "Would reuse"
            print(f"{action} folder: {args.folder}")
        print("Run again with --apply to create the protocol design.")
        return 0

    load_env()
    sdk = load_sdk()
    if sdk is None:
        return 1
    JinkoClient, JinkoError = sdk

    try:
        client = JinkoClient()
        folder = resolve_folder(client, args.folder, create=args.create_folder)
        protocol = client.create_protocol_design_from_csv(
            csv_file_path=csv_path,
            folder=folder,
            name=args.name,
            description=args.description,
        )
        print(f"Created protocol design {protocol.sid}")
        if folder is not None:
            print(f"Folder: {folder.path}")
        if getattr(protocol, "url", None):
            print(protocol.url)
        return 0
    except (ValueError, JinkoError) as exc:
        print(f"Protocol design creation failed: {exc}", file=sys.stderr)
        return 2


if __name__ == "__main__":
    raise SystemExit(main())

SHA-256: 802e30d4447620940c1c2ba0ad7105bec9cb00f238979666d3ce724ab2ac8c7c