This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
CyVerse Data Commons — tools for managing and migrating datasets between the CyVerse Discovery Environment (DE), CKAN data catalog, and cloud storage (AWS S3, Google Cloud Storage). Two main components:
- kando/ — Python/Gradio web app for dataset migration, metadata generation (DCAT/Croissant JSON-LD), and cloud bucket replication to CKAN
- ckan/ — Ansible playbook and configuration for deploying CKAN 2.11 on Ubuntu with HTTPS, Solr, and Keycloak OIDC authentication
# Build Docker image
docker build -t cyverse-gradio-app kando
# Run container (Gradio UI on port 7860)
docker run -p 7860:7860 cyverse-gradio-app
# Run locally without Docker
cd kando
pip install -r requirements.txt
python app.py
# Bulk migration script (migrates curated commons_repo datasets to CKAN)
cd kando
python bulk_migration.py <username> <password># Run Ansible playbook (requires vault.yml with secrets)
ansible-playbook ckan/ansible_script.ymlkando/
├── app.py # Gradio UI entry point — defines all tabs and request handlers
├── ckan.py # CKAN API client (create datasets, upload files, manage resources)
├── de.py # CyVerse DE API client (auth, metadata retrieval, file listing)
├── bulk_migration.py # CLI script for batch-migrating commons_repo directories
├── helpers/
│ ├── migration.py # Metadata cleaning: licenses, tags, dataset name normalization
│ ├── croissant.py # Croissant JSON-LD metadata generator
│ ├── dcat.py # DCAT JSON-LD metadata generator
│ ├── check_metadata_availability.py # Validates DE metadata completeness
│ └── validate_dcat_json.py # DCAT schema validation
├── utils/
│ ├── file.py # File utilities: metadata extraction, CSV→Parquet, JSON-LD generation
│ ├── log.py # In-memory logging (StringIO handler) for validation output
│ └── migrate.py # Orchestrates full DE→CKAN migration pipeline
├── sync/
│ ├── sync_avu.py # AVU sync orchestrator — CLI entry point, source config, sync loop
│ ├── irods_client.py # Terrain API client — auth, directory listing, AVU metadata retrieval
│ ├── mapping.py # Pure functions: AVU metadata → CKAN dataset dict transformation
│ └── state.py # JSON state manifest for incremental sync tracking
├── aws/aws_main.py # AWS S3 bucket → CKAN replication
└── gcs/gcs_main.py # GCS bucket → CKAN replication
- User authenticates via DE credentials →
de.pygets API token - Metadata fetched from DE → validated by
check_metadata_availability.py - Migration:
utils/migrate.pyorchestrates download from DE, optional CSV→Parquet conversion, dataset creation in CKAN viackan.py, and resource linking - Metadata export:
helpers/croissant.pyorhelpers/dcat.pygenerates JSON-LD files
The sync/ module provides automated, incremental synchronization of iRODS collections to CKAN via the Terrain API — including both AVU metadata and resource (file) links. This replicates what utils/migrate.py does interactively, but in batch for all datasets:
- Authenticate →
irods_client.pycallsGET /terrain/token/keycloakwith DE credentials to get a Bearer token - List collections →
GET /terrain/secured/filesystem/directory?path=<base_path>returns subdirectories with folder IDs and timestamps - Filter (ESIIL/NCEMS only) → anonymous WebDAV HEAD request checks if each folder is publicly readable
- Fetch AVUs →
GET /terrain/filesystem/<folder-id>/metadatareturns{"avus": [{"attr": "title", "value": "..."}, ...]}per collection - Map →
mapping.pytransforms AVU key-value pairs to CKAN dataset fields (title, author, license, tags, extras, citation) - Create/update dataset →
package_createorpackage_updatein CKAN, assigned to the correct organization (cyverse,esiil, orncems) - Sync resource links →
GET /terrain/secured/filesystem/paged-directory?path=<collection>lists files/folders, thenresource_createadds WebDAV download URLs (e.g.,https://data.cyverse.org/dav-anon/...) to CKAN, deduplicated by URL - Track state → JSON manifests record each dataset's
modify_timeand CKAN ID for incremental runs
python -m kando.sync.sync_avu --source all # sync curated + esiil + ncems
python -m kando.sync.sync_avu --source esiil # sync esiil only
python -m kando.sync.sync_avu --dry-run # preview without CKAN writes
echo '{"datasets": {}}' > kando/sync_state_curated.json # force full re-syncAnsible playbook (ckan/ansible_script.yml) provisions: PostgreSQL → Solr 9.5 → CKAN 2.11 → Nginx (HTTPS) → Supervisor. Authentication via Keycloak OIDC (ckanext-oidc-pkce). Target domain: dc.cyverse.org.
Kando requires these in .env (see kando/example.env):
TERRAIN_URL— CyVerse Terrain API URL (default:https://de.cyverse.org/terrain)WEB_DAV_URL— WebDAV URL for file accessCKAN_URL— Target CKAN instance URLCKAN_API_KEY— CKAN API key for dataset operationsDE_USERNAME— CyVerse username (required for AVU sync)DE_PASSWORD— CyVerse password (required for AVU sync)
- gradio — Web UI framework
- mlcroissant — Croissant metadata validation
- rdflib — RDF/DCAT metadata handling
- boto3 — AWS S3 integration
- google-cloud-storage — GCS integration
- pyarrow/pandas — CSV→Parquet conversion
- python-dotenv — Environment config