#!/usr/bin/env node
// HUMMBL CSV Relationship Import Script
// Bulk imports relationships from CSV for validation workflow
import { createD1Client } from "../storage/d1-client.js";
import { isRelationshipType, isDirection, isConfidence, isReviewStatus } from "../types/relationships.js";
import type { D1Database } from "@cloudflare/workers-types";
interface Env {
DB: D1Database;
}
interface CSVRelationship {
id: string;
model_a: string;
model_b: string;
relationship_type: string;
direction: string;
confidence: string;
logical_derivation: string;
has_literature_support?: string;
literature_citation?: string;
literature_url?: string;
empirical_observation?: string;
validated_by: string;
validated_at: string;
review_status?: string;
notes?: string;
}
// Simple CSV parser (for basic CSV format)
function parseCSV(csvText: string): CSVRelationship[] {
const lines = csvText.trim().split('\n');
const headers = lines[0].split(',').map(h => h.trim());
const relationships: CSVRelationship[] = [];
for (let i = 1; i < lines.length; i++) {
const values = lines[i].split(',').map(v => v.trim());
if (values.length !== headers.length) continue;
const relationship: any = {};
headers.forEach((header, index) => {
const value = values[index];
relationship[header] = value === '' ? undefined : value;
});
relationships.push(relationship as CSVRelationship);
}
return relationships;
}
async function importRelationshipsFromCSV(env: Env, csvText: string, dryRun = false) {
const db = createD1Client(env.DB);
const relationships = parseCSV(csvText);
console.log(`📄 Parsed ${relationships.length} relationships from CSV`);
if (dryRun) {
console.log("🔍 DRY RUN - No data will be inserted");
}
let successCount = 0;
let errorCount = 0;
const errors: string[] = [];
for (const relationship of relationships) {
try {
// Validate required fields
if (!relationship.id || !relationship.model_a || !relationship.model_b ||
!relationship.relationship_type || !relationship.direction ||
!relationship.logical_derivation || !relationship.validated_by) {
throw new Error("Missing required fields");
}
// Validate enums
if (!isRelationshipType(relationship.relationship_type)) {
throw new Error(`Invalid relationship type: ${relationship.relationship_type}`);
}
if (!isDirection(relationship.direction)) {
throw new Error(`Invalid direction: ${relationship.direction}`);
}
if (relationship.confidence && !isConfidence(relationship.confidence)) {
throw new Error(`Invalid confidence: ${relationship.confidence}`);
}
if (relationship.review_status && !isReviewStatus(relationship.review_status)) {
throw new Error(`Invalid review status: ${relationship.review_status}`);
}
if (!dryRun) {
const result = await db.createRelationship({
id: relationship.id,
model_a: relationship.model_a.toUpperCase(),
model_b: relationship.model_b.toUpperCase(),
relationship_type: relationship.relationship_type,
direction: relationship.direction,
confidence: relationship.confidence || 'U',
logical_derivation: relationship.logical_derivation,
has_literature_support: relationship.has_literature_support === '1' ||
relationship.has_literature_support?.toLowerCase() === 'true' ? 1 : 0,
literature_citation: relationship.literature_citation,
literature_url: relationship.literature_url,
empirical_observation: relationship.empirical_observation,
validated_by: relationship.validated_by,
validated_at: relationship.validated_at,
review_status: relationship.review_status || 'draft',
notes: relationship.notes,
});
if (!result.ok) {
throw new Error(result.error);
}
}
successCount++;
console.log(`✅ ${dryRun ? 'Validated' : 'Imported'} relationship ${relationship.id}: ${relationship.model_a} ${relationship.relationship_type} ${relationship.model_b}`);
} catch (error) {
errorCount++;
const errorMsg = `❌ Failed to ${dryRun ? 'validate' : 'import'} relationship ${relationship.id}: ${error instanceof Error ? error.message : 'Unknown error'}`;
errors.push(errorMsg);
console.error(errorMsg);
}
}
console.log(`\n📊 Import complete:`);
console.log(` ✅ ${successCount} relationships ${dryRun ? 'validated' : 'imported'}`);
console.log(` ❌ ${errorCount} errors`);
if (errors.length > 0) {
console.log("\n🚨 Errors:");
errors.forEach(error => console.log(` ${error}`));
}
return { successCount, errorCount, errors };
}
// CSV Template
export const CSV_TEMPLATE = `id,model_a,model_b,relationship_type,direction,confidence,logical_derivation,has_literature_support,literature_citation,literature_url,empirical_observation,validated_by,validated_at,review_status,notes
R011,DE2,SY2,enables,a→b,B,Systems Thinking requires understanding feedback loops and Second-Order Thinking provides the temporal depth needed to trace system behaviors over time.,1,"Senge, P. (1990). The Fifth Discipline","https://en.wikipedia.org/wiki/The_Fifth_Discipline",Complex system failures often stem from second-order effects that first-order thinking misses.,Reuben Bowlby,2025-11-28T00:00:00.000Z,draft,Initial import from validation exercise`;
export default { importRelationshipsFromCSV, CSV_TEMPLATE };
// For local development
if (import.meta.main) {
console.log("🚀 HUMMBL CSV Relationship Importer");
console.log("Usage: Read CSV from stdin and pipe to this script");
console.log("Example: cat relationships.csv | npx wrangler d1 execute <database> --file=./src/scripts/import-relationships-csv.ts");
console.log("\n📄 CSV Format template:");
console.log(CSV_TEMPLATE);
}