church-api/pocketbase_data.sh
Benjamin Slingo 0c06e159bb Initial commit: Church API Rust implementation
Complete church management system with bulletin management, media processing, live streaming integration, and web interface. Includes authentication, email notifications, database migrations, and comprehensive test suite.
2025-08-19 20:56:41 -04:00

315 lines
10 KiB
Bash
Executable file

#!/bin/bash
# PocketBase to PostgreSQL Migration Script
set -e
echo "🚀 Migrating PocketBase data to PostgreSQL..."
# Configuration
POCKETBASE_URL="http://localhost:8090" # Adjust if different
POSTGRES_URL="$DATABASE_URL"
MIGRATION_DIR="/tmp/pb_migration"
API_URL="https://api.rockvilletollandsda.church"
# Create migration directory
mkdir -p "$MIGRATION_DIR"
cd "$MIGRATION_DIR"
echo "📦 Step 1: Export data from PocketBase..."
# Function to export PocketBase collection data
export_collection() {
local collection=$1
echo " Exporting $collection..."
# Get all records from collection (adjust perPage if you have many records)
curl -s "${POCKETBASE_URL}/api/collections/${collection}/records?perPage=500" \
-o "${collection}.json"
if [ $? -eq 0 ]; then
echo " ✅ Exported $(jq '.items | length' ${collection}.json) records from $collection"
else
echo " ❌ Failed to export $collection"
fi
}
# Export all collections
export_collection "bulletins"
export_collection "events"
export_collection "pending_events"
export_collection "config"
export_collection "bible_verses"
export_collection "Quarterly_Schedule"
export_collection "Offering_and_Sunset_Times_Schedule"
export_collection "rtsda_android"
echo "📥 Step 2: Transform and import data..."
# Create Python script for data transformation
cat > transform_data.py << 'EOF'
import json
import sys
import uuid
from datetime import datetime
import psycopg2
from psycopg2.extras import RealDictCursor
import os
# Database connection
conn = psycopg2.connect(os.environ['DATABASE_URL'])
cur = conn.cursor(cursor_factory=RealDictCursor)
def load_json(filename):
try:
with open(filename, 'r') as f:
data = json.load(f)
return data.get('items', [])
except FileNotFoundError:
print(f"⚠️ {filename} not found, skipping...")
return []
def convert_date(pb_date):
"""Convert PocketBase date to PostgreSQL format"""
if not pb_date:
return None
try:
# PocketBase uses ISO format
dt = datetime.fromisoformat(pb_date.replace('Z', '+00:00'))
return dt
except:
return None
def generate_uuid():
"""Generate PostgreSQL-compatible UUID"""
return str(uuid.uuid4())
print("🔄 Transforming bulletins...")
bulletins = load_json('bulletins.json')
for bulletin in bulletins:
cur.execute("""
INSERT INTO bulletins (id, title, date, url, pdf_url, is_active, pdf_file,
sabbath_school, divine_worship, scripture_reading, sunset,
cover_image, created_at, updated_at)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
ON CONFLICT (id) DO NOTHING
""", (
generate_uuid(),
bulletin.get('title'),
convert_date(bulletin.get('date')),
bulletin.get('url'),
bulletin.get('pdf_url'),
bulletin.get('is_active', True),
bulletin.get('pdf'),
bulletin.get('sabbath_school'),
bulletin.get('divine_worship'),
bulletin.get('scripture_reading'),
bulletin.get('sunset'),
bulletin.get('cover_image'),
convert_date(bulletin.get('created')),
convert_date(bulletin.get('updated'))
))
print("🔄 Transforming events...")
events = load_json('events.json')
for event in events:
cur.execute("""
INSERT INTO events (id, title, description, start_time, end_time, location,
location_url, image, thumbnail, category, is_featured,
recurring_type, approved_from, created_at, updated_at)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
ON CONFLICT (id) DO NOTHING
""", (
generate_uuid(),
event.get('title'),
event.get('description'),
convert_date(event.get('start_time')),
convert_date(event.get('end_time')),
event.get('location'),
event.get('location_url'),
event.get('image'),
event.get('thumbnail'),
event.get('category'),
event.get('is_featured', False),
event.get('reoccuring'), # Note: PB uses 'reoccuring', PG uses 'recurring_type'
event.get('approved_from'),
convert_date(event.get('created')),
convert_date(event.get('updated'))
))
print("🔄 Transforming pending events...")
pending_events = load_json('pending_events.json')
for event in pending_events:
cur.execute("""
INSERT INTO pending_events (id, title, description, start_time, end_time, location,
location_url, image, thumbnail, category, is_featured,
recurring_type, approval_status, submitted_at, bulletin_week,
admin_notes, submitter_email, email_sent, pending_email_sent,
rejection_email_sent, approval_email_sent, created_at, updated_at)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
ON CONFLICT (id) DO NOTHING
""", (
generate_uuid(),
event.get('title'),
event.get('description'),
convert_date(event.get('start_time')),
convert_date(event.get('end_time')),
event.get('location'),
event.get('location_url'),
event.get('image'),
event.get('thumbnail'),
event.get('category'),
event.get('is_featured', False),
event.get('reoccuring'),
event.get('approval_status', 'pending'),
convert_date(event.get('submitted_at')),
event.get('bulletin_week'),
event.get('admin_notes'),
event.get('submitter_email'),
event.get('email_sent', False),
event.get('pending_email_sent', False),
event.get('rejection_email_sent', False),
event.get('approval_email_sent', False),
convert_date(event.get('created')),
convert_date(event.get('updated'))
))
print("🔄 Transforming church config...")
configs = load_json('config.json')
for config in configs:
cur.execute("""
INSERT INTO church_config (id, church_name, contact_email, contact_phone,
church_address, po_box, google_maps_url, about_text,
api_keys, created_at, updated_at)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
ON CONFLICT (id) DO NOTHING
""", (
generate_uuid(),
config.get('church_name'),
config.get('contact_email'),
config.get('contact_phone'),
config.get('church_address'),
config.get('po_box'),
config.get('google_maps_url'),
config.get('about_text'),
json.dumps(config.get('api_key', {})),
convert_date(config.get('created')),
convert_date(config.get('updated'))
))
print("🔄 Transforming bible verses...")
verses = load_json('bible_verses.json')
for verse_record in verses:
cur.execute("""
INSERT INTO bible_verses (id, verses, created_at, updated_at)
VALUES (%s, %s, %s, %s)
ON CONFLICT (id) DO NOTHING
""", (
generate_uuid(),
json.dumps(verse_record.get('verses', {})),
convert_date(verse_record.get('created')),
convert_date(verse_record.get('updated'))
))
print("🔄 Transforming schedules...")
# Quarterly schedules
quarterly = load_json('Quarterly_Schedule.json')
for schedule in quarterly:
cur.execute("""
INSERT INTO schedules (id, schedule_type, year, quarter, schedule_data, created_at, updated_at)
VALUES (%s, %s, %s, %s, %s, %s, %s)
ON CONFLICT (id) DO NOTHING
""", (
generate_uuid(),
'quarterly',
schedule.get('year'),
schedule.get('quarter'),
json.dumps(schedule.get('schedule_data', {})),
convert_date(schedule.get('created')),
convert_date(schedule.get('updated'))
))
# Offering and sunset schedules
offering = load_json('Offering_and_Sunset_Times_Schedule.json')
for schedule in offering:
cur.execute("""
INSERT INTO schedules (id, schedule_type, year, quarter, schedule_data, created_at, updated_at)
VALUES (%s, %s, %s, %s, %s, %s, %s)
ON CONFLICT (id) DO NOTHING
""", (
generate_uuid(),
'offering_sunset',
schedule.get('year'),
None,
json.dumps(schedule.get('schedule_data', {})),
convert_date(schedule.get('created')),
convert_date(schedule.get('updated'))
))
print("🔄 Transforming app versions...")
app_versions = load_json('rtsda_android.json')
for app in app_versions:
cur.execute("""
INSERT INTO app_versions (id, platform, version_name, version_code, download_url,
update_required, description, created_at, updated_at)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
ON CONFLICT (id) DO NOTHING
""", (
generate_uuid(),
'android',
app.get('version_name'),
app.get('version_code'),
None, # You'll need to set download URLs manually
app.get('update_required', False),
app.get('update_description'),
convert_date(app.get('created')),
convert_date(app.get('updated'))
))
# Commit all changes
conn.commit()
cur.close()
conn.close()
print("✅ Data transformation complete!")
EOF
# Install required Python packages
pip3 install psycopg2-binary > /dev/null 2>&1
# Run the transformation
python3 transform_data.py
echo "📊 Step 3: Verifying migration..."
# Check what was migrated
psql "$POSTGRES_URL" -c "
SELECT
'bulletins' as table_name, COUNT(*) as records FROM bulletins
UNION ALL SELECT
'events', COUNT(*) FROM events
UNION ALL SELECT
'pending_events', COUNT(*) FROM pending_events
UNION ALL SELECT
'church_config', COUNT(*) FROM church_config
UNION ALL SELECT
'bible_verses', COUNT(*) FROM bible_verses
UNION ALL SELECT
'schedules', COUNT(*) FROM schedules
UNION ALL SELECT
'app_versions', COUNT(*) FROM app_versions;
"
echo "🎉 Migration complete!"
echo ""
echo "📋 Next steps:"
echo "1. Verify data looks correct in PostgreSQL"
echo "2. Test API endpoints to ensure data is accessible"
echo "3. Update any file URLs that point to PocketBase"
echo "4. Shut down PocketBase once everything is working"
echo ""
echo "🧪 Test your migrated data:"
echo " curl $API_URL/api/bulletins"
echo " curl $API_URL/api/events"
# Cleanup
rm -rf "$MIGRATION_DIR"