Repository navigation
Description
Overview
Implement automated integration with multiple external data sources to populate species external links and images for species that have breeding submissions in the system.
Goals
- Priority: Automation first - build robust tools for continuous sync
- Data Sources: FishBase API, IUCN Red List (enhance existing), Wikipedia/Wikidata, GBIF
- Image Storage: External URLs only (no R2 hosting initially)
- Initial Scope: Species with submissions first, then expand
Current State
Existing Infrastructure
- ✅ Normalized tables exist:
species_external_references,species_images - ✅ IUCN integration fully implemented (
src/integrations/iucn.ts) - ✅ Database functions for managing external references and images
- ✅ MCP tools for species management
⚠️ Tables are empty - no data migration from old JSON columns
Database Schema
-- species_external_references
CREATE TABLE species_external_references (
id INTEGER PRIMARY KEY,
group_id INTEGER REFERENCES species_name_group(group_id) ON DELETE CASCADE,
reference_url TEXT NOT NULL,
display_order INTEGER NOT NULL DEFAULT 0,
UNIQUE(group_id, reference_url)
);
-- species_images
CREATE TABLE species_images (
id INTEGER PRIMARY KEY,
group_id INTEGER REFERENCES species_name_group(group_id) ON DELETE CASCADE,
image_url TEXT NOT NULL,
display_order INTEGER NOT NULL DEFAULT 0,
UNIQUE(group_id, image_url)
);Implementation Plan
Phase 1: Foundation & Infrastructure
1.1 Base Integration Client
File: src/integrations/base-integration-client.ts
Create reusable base class with:
- Rate limiting (configurable per service)
- Retry logic with exponential backoff
- Error handling and logging
- Timeout configuration
- Request caching (optional)
Pattern similar to existing IUCN client.
1.2 Configuration
File: src/config.json
Add section:
{
"externalDataSources": {
"fishbase": {
"enabled": true,
"baseUrl": "https://fishbase.ropensci.org",
"rateLimit": 1000,
"timeout": 10000
},
"wikipedia": {
"enabled": true,
"baseUrl": "https://en.wikipedia.org/api/rest_v1",
"wikidataUrl": "https://www.wikidata.org/w/api.php",
"rateLimit": 100,
"timeout": 10000
},
"gbif": {
"enabled": true,
"baseUrl": "https://api.gbif.org/v1",
"rateLimit": 100,
"timeout": 10000
}
}
}1.3 Sync Logging Table
Migration: src/db/migrations/XXX-external-data-sync-log.sql
CREATE TABLE external_data_sync_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
group_id INTEGER NOT NULL REFERENCES species_name_group(group_id) ON DELETE CASCADE,
source TEXT NOT NULL, -- 'fishbase', 'wikipedia', 'gbif', 'iucn'
sync_date TEXT NOT NULL,
status TEXT NOT NULL, -- 'success', 'error', 'not_found', 'skipped'
links_added INTEGER DEFAULT 0,
images_added INTEGER DEFAULT 0,
error_message TEXT,
FOREIGN KEY (group_id) REFERENCES species_name_group(group_id) ON DELETE CASCADE
);
CREATE INDEX idx_external_sync_group ON external_data_sync_log(group_id);
CREATE INDEX idx_external_sync_source ON external_data_sync_log(source);
CREATE INDEX idx_external_sync_date ON external_data_sync_log(sync_date);
-- Add last sync timestamp to species table
ALTER TABLE species_name_group ADD COLUMN last_external_sync TEXT;Phase 2: Data Source Integrations
2.1 FishBase Integration
File: src/integrations/fishbase.ts
API endpoints:
- Species search:
/species?genus={genus}&species={species} - Species detail:
/species/{id} - Images: Species detail includes photos array
Extract:
- FishBase species page URL
- Species images (photo URLs)
- Related taxonomy links
- Common names (can enhance existing data)
2.2 Wikipedia/Wikidata Integration
File: src/integrations/wikipedia.ts
Process:
- Query Wikidata SPARQL for species by scientific name
- Extract Wikipedia article URL (multiple languages if available)
- Fetch Wikipedia page data via REST API
- Extract infobox images (check for CC licenses)
- Get Wikidata entity URL
Return:
- Wikipedia article URLs (en.wikipedia.org preferred)
- Wikidata entity URL (wikidata.org/wiki/Q...)
- Images from infobox (prioritize CC-licensed)
2.3 GBIF Integration
File: src/integrations/gbif.ts
API endpoints:
- Species match:
/species/match?genus={genus}&species={species} - Species detail:
/species/{key} - Images:
/species/{key}/media
Extract:
- GBIF species page URL
- Occurrence maps
- Specimen photos
- Type specimen images
2.4 Enhanced IUCN Integration
File: src/integrations/iucn.ts (enhance existing)
Currently fetches:
- Conservation status
- Population trend
- Red List URL
Add:
- Habitat descriptions
- Threat information
- IUCN images (if available)
Phase 3: Database Functions
3.1 Sync Log Functions
File: src/db/external-data-sync.ts
// Log a sync operation
recordExternalDataSync(
groupId: number,
source: string,
status: 'success' | 'error' | 'not_found' | 'skipped',
linksAdded: number,
imagesAdded: number,
errorMessage?: string
): Promise<void>
// Get sync history for a species
getSpeciesSyncLog(groupId: number): Promise<SyncLogEntry[]>
// Get species needing sync (never synced or old data)
getSpeciesNeedingExternalSync(
daysOld: number = 90
): Promise<SpeciesNameGroup[]>
// Get sync statistics
getExternalSyncStats(): Promise<{
totalSpecies: number;
speciesWithData: number;
bySouce: Record<string, number>;
lastSyncDate: string;
}>Phase 4: Sync Scripts
4.1 Main Sync Script
File: scripts/sync-external-species-data.ts
CLI arguments:
--species-id <id>- Sync single species--batch <size>- Batch size (default 50)--source <name>- Sync specific source only--submissions-only- Only species with submissions (default)--force- Re-sync even if recently synced--dry-run- Show what would be synced without making changes
Process:
- Get list of species to sync (with submissions, prioritized)
- For each species:
- Query all enabled data sources in parallel
- Merge results (deduplicate URLs)
- Update
species_external_referencestable - Update
species_imagestable - Record sync in
external_data_sync_log - Log progress and errors
- Generate summary report
4.2 Individual Source Scripts
Files:
scripts/sync-fishbase-data.tsscripts/sync-wikipedia-data.tsscripts/sync-gbif-data.ts
For testing and debugging individual integrations.
Phase 5: MCP Tools
5.1 Extend Species MCP Server
File: src/mcp/species-server-core.ts
Add tools:
{
name: "sync_external_data",
description: "Sync external links and images for species",
inputSchema: {
type: "object",
properties: {
group_id: { type: "number", description: "Species group ID (optional)" },
source: {
type: "string",
enum: ["fishbase", "wikipedia", "gbif", "iucn", "all"],
description: "Data source to sync (default: all)"
},
force: { type: "boolean", description: "Force re-sync even if recent" }
}
}
}
{
name: "get_external_data_sync_log",
description: "Get sync log for a species or all species",
inputSchema: {
type: "object",
properties: {
group_id: { type: "number", description: "Species group ID (optional)" },
source: { type: "string", description: "Filter by source (optional)" },
limit: { type: "number", description: "Max results (default 100)" }
}
}
}
{
name: "get_species_missing_external_data",
description: "Find species with submissions but no external data",
inputSchema: {
type: "object",
properties: {
min_submissions: { type: "number", description: "Minimum submissions (default 1)" }
}
}
}
{
name: "get_external_data_stats",
description: "Get statistics on external data population",
inputSchema: { type: "object", properties: {} }
}Phase 6: Admin UI
6.1 Species Detail Page Enhancement
File: src/views/species/detail.pug
Add sections:
- External Links (with favicons for each source)
- Species Images (gallery view)
- Sync Status (last updated, data sources)
- Admin action: "Refresh External Data" button
6.2 External Data Sync Dashboard
File: src/routes/admin/external-data-sync.ts
View: src/views/admin/external-data-sync.pug
Dashboard showing:
- Overall sync statistics
- Species missing external data (with submission counts)
- Recent sync log entries
- Manual sync trigger form
- Data source status (enabled/disabled, last successful sync)
Testing Strategy
Unit Tests
- Integration client base class
- Each data source integration (mock API responses)
- Database sync log functions
- URL deduplication logic
Integration Tests
- End-to-end sync for test species
- Error handling (API failures, rate limits, timeouts)
- Concurrent syncs (race conditions)
Manual Testing
- Test with known species (e.g., Corydoras paleatus, Poecilia reticulata)
- Verify images display correctly
- Check rate limiting doesn't cause issues
- Validate external links work
Implementation Order
-
Foundation (1-2 days)
- Base integration client
- Sync log table and database functions
- Configuration structure
-
FishBase Integration (2-3 days)
- Highest value for fish species
- Test with species with submissions
- Validate data quality
-
Sync Script (1-2 days)
- Main orchestrator script
- Logging and error handling
- Batch processing
-
Wikipedia Integration (1-2 days)
- Good coverage for common species
- Free images
-
GBIF Integration (1-2 days)
- Scientific occurrence data
- Media URLs
-
MCP Tools (1 day)
- Extend species server
- Add sync tools
- Testing
-
Admin UI (2-3 days)
- Species detail enhancements
- Sync dashboard
- Manual trigger controls
Success Metrics
- Coverage: >80% of species with submissions have at least one external link
- Images: >50% of species with submissions have at least one image
- Reliability: <5% sync error rate
- Performance: Can sync 1000 species in <30 minutes (with rate limiting)
- Freshness: All species synced within last 90 days
Future Enhancements
- Download and host images in Cloudflare R2 (copyright permitting)
- Add more data sources (iNaturalist, Encyclopedia of Life)
- Auto-sync on new species creation
- Image quality scoring and auto-selection of best image
- Expand to all species (not just those with submissions)
Related Issues
- Migration of existing JSON data to normalized tables (if any exists)
- Admin UI for manually editing external references and images
Activity
jra3 commentedon Nov 18, 2025
Implementation Progress Update
I've completed Phase 1 (Foundation) and most of Phase 2 (Data Source Integrations) for the automated external species data integration project. Here's what's been implemented:
✅ Completed
Phase 1: Foundation & Infrastructure
BaseIntegrationClientclass (src/integrations/base-integration-client.ts)src/config.jsonandsrc/config.sample.jsondb/migrations/048-add-external-data-sync-log.sql)external_data_sync_logtable for tracking sync operationslast_external_synctimestamp field onspecies_name_grouptablesrc/db/external-data-sync.ts)recordExternalDataSync()- Log sync attemptsgetSpeciesSyncLog()- Retrieve sync historygetSpeciesWithMissingExternalData()- Find species needing syncgetSpeciesNeedingExternalSync()- Find stale datagetExternalDataSyncStats()- Statistics dashboard dataPhase 2: FishBase Integration
src/integrations/fishbase.ts)BaseIntegrationClientscripts/sync-fishbase-external-data.ts)--batch,--dry-run,--force,--species-idThe FishBase rOpenSci API (
https://fishbase.ropensci.org) currently has SSL certificate issues:This prevents direct API access from Node.js/fetch without disabling certificate validation (not recommended for production).
🔄 Alternative Approaches for FishBase
Given the SSL issue, here are recommended alternatives:
Option A: Use Existing DuckDB Integration (Recommended)
scripts/fishbase/with importers for common names and reference linksspecies_external_referencesandspecies_imagestablesOption B: Use FishBase Website Scraping
https://www.fishbase.se/summary/{SpecCode}Option C: Wait for API Fix or Use Alternative Endpoint
📋 Recommended Next Steps
Create DuckDB-based FishBase sync script (similar to existing importers)
species_external_referenceswith FishBase URLsspecies_imageswith image URLs from FishBase dataImplement Wikipedia/Wikidata Integration
Implement GBIF Integration
Add MCP Tools
sync_external_data,get_external_data_stats, etc.Build Admin UI
💡 Implementation Recommendation
I recommend starting with Option A (DuckDB-based FishBase sync) since:
scripts/fishbase/importers/Would you like me to proceed with creating a DuckDB-based FishBase external data sync script?
jra3 commentedon Nov 18, 2025
✅ DuckDB-Based FishBase Sync Completed!
I've successfully created and tested a DuckDB-based FishBase external data sync script that works around the SSL certificate issues with the FishBase API.
What Was Built
New Script:
scripts/sync-fishbase-external-data-duckdb.tsThis script:
species_external_referencesandspecies_imagesexternal_data_sync_logtableTest Results
Initial sync of 27 species with submissions:
external_data_sync_loglast_external_synctimestampSuccess rate: 75% (27 found / 36 attempted)
Usage Examples
Sample Data
External References:
Images for Poecilia reticulata (5 images):
Key Features
Database Schema Usage
The script properly uses the normalized tables:
species_external_references:
group_id(FK to species_name_group)reference_url(the FishBase URL)display_order(incremental from existing max)species_images:
group_id(FK to species_name_group)image_url(the FishBase image URL)display_order(incremental from existing max)external_data_sync_log:
group_id,source('fishbase'),sync_date,status('success')links_added,images_added(counts for statistics)Next Steps
Production Deployment
Since the script works locally with DuckDB and the production server is under-provisioned:
Option 1: Run sync locally, then use MCP to update production database
Option 2: Add cron job to run sync weekly on a more powerful machine
Option 3: Integrate MCP tools to trigger selective syncs from admin UI
The infrastructure is now in place for automated external species data enrichment! 🎉
jra3 commentedon Nov 18, 2025
✅ Full Sync Completed - Step 1 Done!
Successfully completed a full sync of all fish species with approved submissions.
Results
Coverage:
Performance:
Top Species Synced
Species Not Found (9)
Primarily subspecies/variants not in FishBase:
Needs Investigation:
Database Stats
Next: Step 2 - MCP Production Sync
Ready to use MCP to sync this data to production. The data is clean, validated, and ready to deploy! 🚀
Full report saved:
/tmp/fishbase-sync-report.mdjra3 commentedon Nov 18, 2025
🎉 Phase 1 Complete: FishBase Integration DONE!
Successfully implemented and tested complete FishBase external data integration with image hosting!
✅ What Was Accomplished
Infrastructure (Commit 7618f24):
FishBase Integration:
Image Processing:
UI Enhancements:
📊 Final Statistics
Local Database:
Coverage:
🚀 Ready for Production
Deployment Package:
PRODUCTION_DEPLOYMENT.mdWhat's Next:
🖼️ Screenshots
[Local Testing Screenshots Available]
📝 Files Created/Modified (11 files, 1829 lines)
Database:
db/migrations/048-add-external-data-sync-log.sqldb/migrations/049-add-image-metadata.sqlsrc/db/external-data-sync.ts(388 lines)Integrations:
src/integrations/base-integration-client.ts(208 lines)src/integrations/fishbase.ts(234 lines)Scripts:
scripts/sync-fishbase-external-data-duckdb.ts(452 lines)scripts/download-and-upload-species-images.ts(300 lines)scripts/sync-species-to-prod-mcp.ts(129 lines)UI:
src/views/species/detail.pug(+56 lines)src/db/species.ts(+10 lines for image metadata)src/config.sample.json(+15 lines for new integrations)🎯 Success Criteria Met
Ready to deploy to production! 🚢
feat: add external species data integration with FishBase
fix: protect species images from cleanup job deletion
fix: handle duplicate synonyms gracefully in species merge
Add integration tests for external data sources