feat: add migration script for value pluralism documents
Adds visibility and order fields to the 3 value pluralism documents
so they display correctly in the docs.html sidebar.
🤖 Generated with Claude Code
This commit is contained in:
parent
1906c0be0b
commit
1cd69829d7
1 changed files with 68 additions and 177 deletions
245
scripts/migrate-value-pluralism-docs.js
Normal file → Executable file
245
scripts/migrate-value-pluralism-docs.js
Normal file → Executable file
|
|
@ -1,203 +1,94 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Migrate Value Pluralism Documents to MongoDB
|
||||
* Adds three new value pluralism documents to the documents collection
|
||||
* Migration: Add visibility and order to value pluralism documents
|
||||
*
|
||||
* This migration ensures the 3 value pluralism documents are properly
|
||||
* configured to display in the docs.html sidebar.
|
||||
*/
|
||||
|
||||
// Load environment variables from .env file
|
||||
require('dotenv').config();
|
||||
const { MongoClient } = require('mongodb');
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
const marked = require('marked');
|
||||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
const config = require('../src/config/app.config');
|
||||
const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017';
|
||||
const DB_NAME = process.env.MONGO_DB || 'tractatus_prod';
|
||||
|
||||
// Document structure from existing documents
|
||||
const documentsToMigrate = [
|
||||
const UPDATES = [
|
||||
{
|
||||
file: 'docs/research/pluralistic-values-research-foundations.md',
|
||||
title: 'Pluralistic Values: Research Foundations',
|
||||
slug: 'pluralistic-values-research-foundations',
|
||||
quadrant: 'STRATEGIC',
|
||||
persistence: 'HIGH',
|
||||
category: 'research',
|
||||
audience: ['researcher', 'technical'],
|
||||
tags: ['value-pluralism', 'research', 'deliberative-democracy', 'ethics', 'philosophy'],
|
||||
description: 'Supporting research material for PluralisticDeliberationOrchestrator implementation, covering deliberative democracy, value pluralism theory, and cross-cultural communication'
|
||||
},
|
||||
{
|
||||
file: 'docs/value-pluralism-faq.md',
|
||||
title: 'Value Pluralism in Tractatus: Frequently Asked Questions',
|
||||
slug: 'value-pluralism-faq',
|
||||
quadrant: null,
|
||||
persistence: 'HIGH',
|
||||
category: 'documentation',
|
||||
audience: ['general'],
|
||||
tags: ['value-pluralism', 'faq', 'documentation', 'ethics'],
|
||||
description: 'Accessible explanation of how Tractatus handles moral disagreement without imposing hierarchy'
|
||||
category: 'getting-started',
|
||||
visibility: 'public',
|
||||
order: 3
|
||||
},
|
||||
{
|
||||
slug: 'pluralistic-values-research-foundations',
|
||||
category: 'research-theory',
|
||||
visibility: 'public',
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
file: 'docs/pluralistic-values-deliberation-plan-v2.md',
|
||||
title: 'Pluralistic Values Deliberation Enhancement Plan',
|
||||
slug: 'pluralistic-values-deliberation-plan-v2',
|
||||
quadrant: 'OPERATIONAL',
|
||||
persistence: 'HIGH',
|
||||
category: 'implementation-guide',
|
||||
audience: ['implementer', 'researcher'],
|
||||
tags: ['value-pluralism', 'implementation', 'deliberation', 'planning'],
|
||||
description: 'Technical design document for implementing non-hierarchical moral reasoning in the Tractatus Framework'
|
||||
category: 'technical-reference',
|
||||
visibility: 'public',
|
||||
order: 12
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Generate table of contents from markdown
|
||||
*/
|
||||
function generateToC(markdown) {
|
||||
const toc = [];
|
||||
const lines = markdown.split('\n');
|
||||
async function migrate() {
|
||||
console.log('🔧 Starting value pluralism documents migration...');
|
||||
console.log(` Database: ${DB_NAME}`);
|
||||
console.log('');
|
||||
|
||||
for (const line of lines) {
|
||||
const match = line.match(/^(#{1,6})\s+(.+)$/);
|
||||
if (match) {
|
||||
const level = match[1].length;
|
||||
const title = match[2].trim();
|
||||
const slug = title
|
||||
.toLowerCase()
|
||||
.replace(/[^\w\s-]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/-+/g, '-')
|
||||
.trim();
|
||||
|
||||
toc.push({ level, title, slug });
|
||||
}
|
||||
}
|
||||
|
||||
return toc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate search index (lowercase, no markdown formatting)
|
||||
*/
|
||||
function generateSearchIndex(markdown) {
|
||||
return markdown
|
||||
.toLowerCase()
|
||||
.replace(/```[\s\S]*?```/g, '') // Remove code blocks
|
||||
.replace(/`[^`]+`/g, '') // Remove inline code
|
||||
.replace(/[#*_\[\]()]/g, '') // Remove markdown formatting
|
||||
.replace(/\n+/g, '\n') // Collapse multiple newlines
|
||||
.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate a single document
|
||||
*/
|
||||
async function migrateDocument(docInfo, db) {
|
||||
console.log(`\nMigrating: ${docInfo.title}`);
|
||||
|
||||
// Read markdown file
|
||||
const markdownPath = path.join(__dirname, '..', docInfo.file);
|
||||
const markdown = await fs.readFile(markdownPath, 'utf-8');
|
||||
|
||||
// Convert to HTML
|
||||
const html = marked.parse(markdown);
|
||||
|
||||
// Generate ToC
|
||||
const toc = generateToC(markdown);
|
||||
console.log(` ✓ Generated ToC (${toc.length} headings)`);
|
||||
|
||||
// Generate search index
|
||||
const searchIndex = generateSearchIndex(markdown);
|
||||
|
||||
// Create document
|
||||
const document = {
|
||||
title: docInfo.title,
|
||||
slug: docInfo.slug,
|
||||
quadrant: docInfo.quadrant,
|
||||
persistence: docInfo.persistence,
|
||||
content_html: html,
|
||||
content_markdown: markdown,
|
||||
toc: toc,
|
||||
metadata: {
|
||||
author: 'System',
|
||||
date_created: new Date(),
|
||||
date_updated: new Date(),
|
||||
version: '1.0',
|
||||
document_code: null,
|
||||
related_documents: [],
|
||||
tags: docInfo.tags,
|
||||
category: docInfo.category,
|
||||
audience: docInfo.audience,
|
||||
description: docInfo.description
|
||||
},
|
||||
translations: {},
|
||||
search_index: searchIndex
|
||||
};
|
||||
|
||||
// Check if document already exists
|
||||
const existing = await db.collection('documents').findOne({ slug: docInfo.slug });
|
||||
|
||||
if (existing) {
|
||||
console.log(` ⚠ Document already exists, updating...`);
|
||||
await db.collection('documents').updateOne(
|
||||
{ slug: docInfo.slug },
|
||||
{ $set: document }
|
||||
);
|
||||
console.log(` ✓ Updated`);
|
||||
} else {
|
||||
await db.collection('documents').insertOne(document);
|
||||
console.log(` ✓ Inserted`);
|
||||
}
|
||||
|
||||
return docInfo.slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main migration function
|
||||
*/
|
||||
async function main() {
|
||||
console.log('=== Value Pluralism Documents Migration ===\n');
|
||||
|
||||
let client;
|
||||
const client = new MongoClient(MONGO_URI);
|
||||
|
||||
try {
|
||||
// Connect to MongoDB
|
||||
console.log('Connecting to MongoDB...');
|
||||
client = await mongoose.connect(config.mongodb.uri, config.mongodb.options);
|
||||
const db = mongoose.connection.db;
|
||||
console.log('✓ Connected\n');
|
||||
await client.connect();
|
||||
const db = client.db(DB_NAME);
|
||||
const collection = db.collection('documents');
|
||||
|
||||
// Migrate each document
|
||||
const migratedSlugs = [];
|
||||
for (const docInfo of documentsToMigrate) {
|
||||
const slug = await migrateDocument(docInfo, db);
|
||||
migratedSlugs.push(slug);
|
||||
for (const update of UPDATES) {
|
||||
const { slug, ...fields } = update;
|
||||
|
||||
const result = await collection.updateOne(
|
||||
{ slug },
|
||||
{ $set: fields }
|
||||
);
|
||||
|
||||
if (result.matchedCount === 0) {
|
||||
console.log(` ⚠️ Document not found: ${slug}`);
|
||||
} else if (result.modifiedCount === 0) {
|
||||
console.log(` ℹ️ Already up to date: ${slug}`);
|
||||
} else {
|
||||
console.log(` ✅ Updated: ${slug}`);
|
||||
console.log(` - category: ${fields.category}`);
|
||||
console.log(` - visibility: ${fields.visibility}`);
|
||||
console.log(` - order: ${fields.order}`);
|
||||
}
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Summary
|
||||
console.log('\n=== Migration Complete ===\n');
|
||||
console.log(`✓ Migrated ${migratedSlugs.length} documents:`);
|
||||
migratedSlugs.forEach(slug => console.log(` - ${slug}`));
|
||||
console.log('\nDocuments are now available in docs.html');
|
||||
console.log('PDFs available at:');
|
||||
migratedSlugs.forEach(slug =>
|
||||
console.log(` - /downloads/${slug}.pdf`)
|
||||
);
|
||||
// Verify
|
||||
console.log('🔍 Verification:');
|
||||
const docs = await collection.find(
|
||||
{ slug: { $in: UPDATES.map(u => u.slug) } },
|
||||
{ projection: { slug: 1, category: 1, visibility: 1, order: 1, _id: 0 } }
|
||||
).toArray();
|
||||
|
||||
docs.forEach(doc => {
|
||||
console.log(` ${doc.slug}:`);
|
||||
console.log(` category: ${doc.category}`);
|
||||
console.log(` visibility: ${doc.visibility}`);
|
||||
console.log(` order: ${doc.order}`);
|
||||
});
|
||||
|
||||
console.log('');
|
||||
console.log('✨ Migration complete!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n✗ Migration failed:', error.message);
|
||||
console.error(error.stack);
|
||||
console.error('❌ Migration failed:', error);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
if (client) {
|
||||
await mongoose.connection.close();
|
||||
console.log('\n✓ Database connection closed');
|
||||
}
|
||||
await client.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Run migration
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = { migrateDocument, generateToC, generateSearchIndex };
|
||||
migrate();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue