Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
a658378
- migrated to GPT-5 & Responses API
RandyHaddad Oct 29, 2025
02faaf6
updated movement research to synchronous
RandyHaddad Oct 29, 2025
95c8bf2
fixed the vision prompt view to restrict the donation to total amount…
RandyHaddad Oct 29, 2025
dc60391
improved results page view
RandyHaddad Oct 29, 2025
6c0b32b
Update config: fix API port and Polkadot node URL, remove old docs
RandyHaddad Nov 8, 2025
1b68039
Update API and Polkadot node URLs in config, comment out old values
RandyHaddad Nov 8, 2025
5782079
Fix deployment config: use env vars for SECRET_KEY/DEBUG, improve ALL…
RandyHaddad Nov 8, 2025
0e1b585
Revert workflow to original CI/CD setup, remove .papi build artifacts…
RandyHaddad Nov 8, 2025
1ec1ec8
Remove all .papi files from git tracking - fully generated during CI/CD
RandyHaddad Nov 8, 2025
393fdd8
Restore manager's .papi config files, only ignore build artifacts (di…
RandyHaddad Nov 8, 2025
c71247b
Revert .papi files to manager's version (ccfd262) - remove accidental…
RandyHaddad Nov 8, 2025
cc1ba45
Fix SQLite database persistence on Render - use persistent disk path
RandyHaddad Nov 8, 2025
7bbd678
Update persistent disk path to /opt/render/project/data (non-reserved…
RandyHaddad Nov 8, 2025
9112f12
Trigger movement research automatically when registering test charities
RandyHaddad Nov 8, 2025
0e12da7
Run movement research synchronously during pre-deploy to ensure movem…
RandyHaddad Nov 8, 2025
c4718bc
Research movements for existing charities that don't have any yet
RandyHaddad Nov 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,11 @@ media/
# Ignore target directories for Polkadot contracts
polkadot_contracts/target/
polkadot_contracts/*/target/
polkadot-react-app/node_modules/
polkadot-react-app/node_modules/

# Polkadot API build artifacts (generated during CI/CD)
# Keep config files but ignore build outputs
eunoia_web/.papi/descriptors/dist/
eunoia_web/.papi/descriptors/src/
# Note: .papi/descriptors/.gitignore, .papi/descriptors/package.json,
# .papi/metadata/dot.scale, and .papi/polkadot-api.json are kept in git
187 changes: 187 additions & 0 deletions AI_FEATURES_EUNOIA.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# AI Features - Eunoia

## Charity Registration

We aim to make the charity registration process as seamless as possible. Our AI-powered system automatically extracts information from charity websites, categorizes charities, and discovers active movements/campaigns.

### Automated AI Processing

When a charity is registered **via the API or web form**, the following happens automatically:

1. **Basic AI Extraction** (via `process_charity_website()` signal)
- Fetches and parses the charity's website
- Uses GPT-5 to extract:
- **Description**: 2-3 paragraph summary of mission and activities
- **Tagline**: Short, catchy phrase (max 15 words)
- **Category**: Automatically assigns from 9 categories (ENV, EDU, HEA, ANI, ART, HUM, COM, DIS, OTH)
- **Keywords**: 5-7 relevant keywords describing the charity's focus
- Generates semantic embedding for search/matching
- Runs synchronously (completes in seconds)

2. **Comprehensive Research** (via `launch_charity_research_in_background()`)
- Crawls charity website (up to 6 pages)
- Uses GPT-4.1 to analyze:
- **Charity Profile**: Detailed profile extraction
- **Movements**: Discovers up to 5 active campaigns/initiatives per charity
- Creates `Movement` objects with:
- Title, summary, category, geography
- Source URLs, confidence scores
- Embeddings for semantic matching
- Runs asynchronously in background (takes minutes)

### Test Charity Setup

For development and testing, we provide scripts to quickly set up test charities:

#### 1. Register Test Charities

By running:

```bash
cd backend/eunoia_backend
python register_test_charities.py
```

This script:
- Creates 10 test charities with diverse categories
- Pre-populates basic fields (name, description, wallet addresses, etc.)
- Triggers basic AI extraction (description, tagline, category, keywords)
- **Note**: Does NOT automatically fetch movements (see below)

**Output:**
```
Creating test charities...
==================================================
✅ Created: Global Water Initiative
Category: Health & Medicine
Location: Global
Status: ✅ VERIFIED
Contact: Sarah Chen
--------------------------------------------------
...
✅ Successfully created 10 test charities!
📊 Total charities in database: 10
```

#### 2. Fetch Movements for Test Charities

Since test charities are created via direct ORM (bypassing API hooks), movements must be fetched manually.

**Option A: Async Mode (Recommended)**
```bash
python trigger_movement_research.py
```

This will:
- Trigger research for all 10 test charities
- Wait for all background threads to complete
- Show progress as each charity finishes
- Display number of movements found per charity

**Option B: Sync Mode (One at a time)**
```bash
python trigger_movement_research.py --wait
```

This runs synchronously, showing results immediately for each charity.

**Option C: Django Management Command**
```bash
python manage.py research_existing_charity --test-only --sync
```

**Output:**
```
🔄 Launching research for: Global Water Initiative (ID: 55)
Website: https://www.charitywater.org
------------------------------------------------------------
⏳ Waiting for background research to complete...
[1/10] Thread completed
✅ Global Water Initiative: 3 movements found
[2/10] Thread completed
✅ Education For All Foundation: 2 movements found
...
```

### Complete Setup Workflow

For a complete test environment setup:

```bash
# 1. Register test charities
python register_test_charities.py

# 2. Fetch movements for all test charities
python trigger_movement_research.py

# 3. Verify movements were created
python manage.py shell
```

```python
# In Django shell
from main.models import Charity, Movement

# Check movements for a charity
charity = Charity.objects.get(name="Global Water Initiative")
print(f"Movements for {charity.name}: {charity.movements.count()}")
for movement in charity.movements.all():
print(f" - {movement.title}: {movement.summary[:50]}...")
```

### What Happens Automatically vs Manually

| Action | When Created Via | What Runs Automatically |
|--------|-----------------|------------------------|
| **API** (`POST /api/charities/`) | REST API | ✅ Basic AI extraction<br>✅ Movement research |
| **Web Form** (`CharityRegistrationForm`) | Django form | ✅ Basic AI extraction<br>✅ Movement research |
| **Test Script** (`register_test_charities.py`) | Direct ORM | ✅ Basic AI extraction<br>❌ **NO** movement research* |

*Movements must be fetched manually using `trigger_movement_research.py`

### AI Models Used

| Feature | Model | Purpose |
|---------|-------|---------|
| Basic extraction | GPT-5 | Description, tagline, category, keywords |
| Embeddings | text-embedding-3-small | Semantic search vectors |
| Charity profile | GPT-4.1 | Detailed profile analysis |
| Movement discovery | GPT-4.1 | Finding active campaigns/initiatives |

### API Endpoints

- **Register Charity**: `POST /api/charities/`
- Automatically triggers AI processing
- Returns charity object with AI-populated fields

- **Get Charities**: `GET /api/charities/`
- Returns all charities with AI-extracted data

- **Semantic Search**: `GET /api/charity-semantic-search/?query=<your_query>`
- Uses embeddings for intelligent matching
- Returns charities ranked by relevance

### Troubleshooting

**Problem**: Movements not appearing after registration
- **Solution**: Use `trigger_movement_research.py` if charity was created via test script
- **Check**: Verify charity has `website_url` set

**Problem**: AI extraction failed
- **Check**: Website URL is accessible and returns HTML
- **Check**: OpenAI API key is set in environment
- **Check**: Logs for specific error messages

**Problem**: Research taking too long
- **Normal**: Each charity can take 2-5 minutes
- **Tip**: Use `--sync` mode to see progress in real-time
- **Check**: Network connectivity and API rate limits

### Best Practices

1. **Always provide website_url**: AI features require a website to extract information
2. **Verify extraction**: Check that `description`, `tagline`, `category`, and `keywords` are populated
3. **Wait for movements**: Allow background research to complete (use `--wait` flag for sync mode)
4. **Monitor logs**: Check Django logs for research progress and errors
5. **Test first**: Use test charities to verify AI features before production use

43 changes: 43 additions & 0 deletions backend/eunoia_backend/delete_test_charities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import os
import django

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'eunoia_backend.settings')
django.setup()

from main.models import Charity

# List of test charity names from register_test_charities.py
test_charity_names = [
'Global Water Initiative',
'Education For All Foundation',
'Wildlife Conservation Network',
'Green Earth Climate Action',
'Community Health Partners',
'Arts & Culture Preservation Society',
'Human Rights Advocacy Network',
'Disaster Relief Response Team',
'Rural Development Initiative',
'Tech For Good Collective'
]

print("Deleting test charities...")
print("=" * 50)

deleted_count = 0
for name in test_charity_names:
try:
charity = Charity.objects.filter(name=name).first()
if charity:
charity.delete()
print(f"✅ Deleted: {name}")
deleted_count += 1
else:
print(f"⚠️ Charity '{name}' not found, skipping...")
except Exception as e:
print(f"❌ Error deleting charity '{name}': {e}")

print("=" * 50)
print(f"✅ Successfully deleted {deleted_count} test charities!")
print(f"📊 Remaining charities in database: {Charity.objects.count()}")


29 changes: 24 additions & 5 deletions backend/eunoia_backend/eunoia_backend/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,21 @@
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-#da0k)&*ea)01gqr)3a_7$g#166x#&q2!$-2if+3zr#a6m(jy6'
SECRET_KEY = os.getenv('SECRET_KEY', 'django-insecure-#da0k)&*ea)01gqr)3a_7$g#166x#&q2!$-2if+3zr#a6m(jy6')

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['eunoia.work', 'www.eunoia.work', 'localhost', '127.0.0.1', '52.37.227.112', 'ec2-52-37-227-112.us-west-2.compute.amazonaws.com', 'eunoia-api-eya2hhfdfzcchyc2.canadacentral-01.azurewebsites.net']
DEBUG = os.getenv('DEBUG', 'False').lower() == 'true'

ALLOWED_HOSTS = [
'eunoia-backend-bebv.onrender.com',
'eunoia.work',
'www.eunoia.work',
'localhost',
'127.0.0.1',
'52.37.227.112',
'ec2-52-37-227-112.us-west-2.compute.amazonaws.com',
'eunoia-api-eya2hhfdfzcchyc2.canadacentral-01.azurewebsites.net'
]

# OpenAI API Key
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
Expand Down Expand Up @@ -120,10 +129,20 @@
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases

# Use persistent disk path on Render, fallback to BASE_DIR for local development
# Render persistent disk should be mounted at a non-reserved path like /opt/render/project/data
if os.getenv('RENDER'):
# On Render, use persistent disk path
# The persistent disk should be mounted at /opt/render/project/data
db_path = '/opt/render/project/data/db.sqlite3'
else:
# Local development
db_path = BASE_DIR / 'db.sqlite3'

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
'NAME': db_path,
}
}

Expand Down
Loading