From 9aeba19c6733d86fe8ad99725436b25016c7093c Mon Sep 17 00:00:00 2001 From: Kimball Bighorse Date: Tue, 30 Dec 2025 02:31:04 -0800 Subject: [PATCH] Documentation exploring migration paths --- docs/ADMIN_ARCHITECTURE_OPTIONS.md | 634 ++++++++++++++++++ docs/ADMIN_FRAMEWORK_EVALUATION.md | 310 +++++++++ .../MS_ACCESS_TO_STARLETTE_ADMIN_MIGRATION.md | 582 ++++++++++++++++ docs/OCOTILLOUI_STARLETTE_ADMIN_PARITY.md | 583 ++++++++++++++++ 4 files changed, 2109 insertions(+) create mode 100644 docs/ADMIN_ARCHITECTURE_OPTIONS.md create mode 100644 docs/ADMIN_FRAMEWORK_EVALUATION.md create mode 100644 docs/MS_ACCESS_TO_STARLETTE_ADMIN_MIGRATION.md create mode 100644 docs/OCOTILLOUI_STARLETTE_ADMIN_PARITY.md diff --git a/docs/ADMIN_ARCHITECTURE_OPTIONS.md b/docs/ADMIN_ARCHITECTURE_OPTIONS.md new file mode 100644 index 00000000..7c824814 --- /dev/null +++ b/docs/ADMIN_ARCHITECTURE_OPTIONS.md @@ -0,0 +1,634 @@ +# Admin UI Architecture Options for NMSampleLocations + +## Context + +**Current Situation:** +- AMPAPI (SQL Server) data needs to migrate to NMSampleLocations (PostgreSQL) +- OcotilloUI (React + Refine.dev) is the current admin interface +- Proposal: Temporarily replace OcotilloUI with Starlette Admin during migration + +**Two Critical Questions:** +1. **Do we need to consider Starlette Admin alternatives?** +2. **Can Starlette Admin exist within NMSampleLocations as a monolithic repo?** + +--- + +## Question 1: Starlette Admin vs. Alternatives + +### Full Spectrum of Options + +We've evaluated framework-based solutions, but let's consider the **complete architectural spectrum**: + +| Option | Description | Pros | Cons | Effort | +|--------|-------------|------|------|--------| +| **1. Starlette Admin** | Python admin framework mounted to FastAPI | Native SQLAlchemy, GeoAlchemy2 support | Less flashy UI, custom views need work | 32-42 hrs | +| **2. Keep OcotilloUI** | Continue using React + Refine.dev | Already built, familiar to staff | Requires maintaining React frontend | 0 hrs (status quo) | +| **3. Simplify OcotilloUI** | Remove unused features, focus on CRUD | Familiar UI, less maintenance | Still requires React/Node.js stack | 16-24 hrs | +| **4. Django Admin (microservice)** | Separate Django service sharing PostgreSQL | Enterprise-grade admin, battle-tested | Additional service to deploy/maintain | 40-60 hrs | +| **5. SQLAdmin** | Lightweight SQLAlchemy admin for FastAPI | Simpler than Starlette Admin | Weaker RBAC, limited geospatial | 24-32 hrs | +| **6. No Admin UI** | Direct database access (psql, pgAdmin, DBeaver) | Zero development effort | Not user-friendly for non-technical staff | 0 hrs | +| **7. Custom FastAPI Endpoints** | Build minimal admin API, no UI | Full control, lightweight | Staff need Postman/curl | 8-12 hrs | +| **8. React Admin (custom)** | Build from scratch with react-admin/Refine | Perfect fit for needs | 3-4 months development time | 300-400 hrs | + +--- + +### Decision Matrix: When to Use Each Option + +#### ✅ **Use Starlette Admin If:** +- You want a **temporary solution** during migration (2-6 months) +- Core CRUD is sufficient for 80% of workflows +- You can defer maps/custom forms to later +- You want Python-only stack (no Node.js) +- Timeline: **2-4 weeks** + +**Verdict**: **Best for migration MVP** - balances speed, functionality, low risk + +--- + +#### ✅ **Keep OcotilloUI If:** +- Staff **cannot tolerate** deferred features (maps, forms) +- Well Inventory Form or Groundwater Level Form are **critical path** +- React expertise is available for maintenance +- You're comfortable maintaining dual stack (Python + Node.js) + +**Verdict**: **Safest option** - no changes, no risk, but doesn't reduce complexity + +--- + +#### ✅ **Simplify OcotilloUI If:** +- You want to **reduce OcotilloUI complexity** but keep React +- Custom forms are critical, but dashboard/apps are not +- You want to keep the map view +- Timeline: **2-3 weeks** to strip down features + +**Pros**: +- Keep the 20% of features you actually use (maps, forms) +- Remove the 80% you don't (coming soon apps, experimental features) + +**Cons**: +- Still maintaining React frontend +- Doesn't help with AMPAPI migration directly + +**Verdict**: **Consider if Starlette Admin fails UAT** + +--- + +#### ✅ **Use Django Admin (Microservice) If:** +- You need **enterprise-grade admin** long-term +- Team has Django expertise +- You're okay with microservice complexity +- Timeline: **6-8 weeks** + +**Architecture**: +``` +┌─────────────────┐ ┌─────────────────┐ +│ Django Admin │ │ NMSampleLocations│ +│ (Port 8001) │────────▶│ PostgreSQL DB │ +│ Admin UI only │ Shares │ │ +└─────────────────┘ DB └─────────────────┘ + ▲ + │ + ┌────────┴────────┐ + │ FastAPI API │ + │ (Port 8000) │ + └─────────────────┘ +``` + +**Pros**: +- Best-in-class admin experience (Django Admin is gold standard) +- Automatic admin for all models +- Built-in user management, permissions, audit logs +- Geospatial admin via django.contrib.gis + +**Cons**: +- **Two separate services** to deploy and maintain +- **Duplicate model definitions** (SQLAlchemy → Django ORM conversion) +- **Authentication sync** complexity +- More moving parts in production + +**Verdict**: **Overkill for temporary migration** - consider for long-term if Starlette Admin proves insufficient + +--- + +#### ✅ **Use SQLAdmin If:** +- You want **simpler** alternative to Starlette Admin +- You don't need complex RBAC +- Geospatial fields are not a priority +- Timeline: **3-4 weeks** + +**Key Differences from Starlette Admin**: + +| Feature | Starlette Admin | SQLAdmin | +|---------|----------------|----------| +| RBAC | ✅ Built-in flexible system | ⚠️ Basic, manual | +| GeoAlchemy2 | ✅ Native support | ⚠️ Basic (shows WKT) | +| Custom Actions | ✅ Decorator-based | ⚠️ Limited | +| UI Customization | ✅ Extensive | ⚠️ Basic | +| File Uploads | ✅ Supported | ✅ Supported | + +**Verdict**: **Fallback if Starlette Admin is too complex** - but unlikely given your needs + +--- + +#### ✅ **No Admin UI If:** +- Users are **highly technical** (can use psql, pgAdmin, DBeaver) +- Data entry volume is **very low** (< 10 records/week) +- You have **strict timeline** (need admin access immediately) + +**Tools**: +- **psql**: Command-line PostgreSQL client +- **pgAdmin 4**: GUI database browser +- **DBeaver**: Universal database tool with PostGIS support + +**Pros**: +- Zero development effort +- Direct database access (no abstraction bugs) +- Full SQL power for complex queries + +**Cons**: +- Not suitable for non-technical staff +- Easy to make mistakes (no validation) +- No audit trail +- No business logic enforcement + +**Verdict**: **Only for developers** - not suitable for data stewards/staff + +--- + +#### ✅ **Custom FastAPI Endpoints If:** +- You need **minimal admin API** for scripts/automation +- No UI needed (staff use Postman, curl, or scripts) +- Timeline: **1 week** + +**Example**: +```python +# api/admin_operations.py +from fastapi import APIRouter, Depends +from schemas.admin import BulkPublishRequest + +router = APIRouter(prefix="/admin", tags=["admin"]) + +@router.post("/bulk-publish") +async def bulk_publish( + request: BulkPublishRequest, + user: User = Depends(require_admin) +): + """Bulk change release_status to 'public' for given location IDs.""" + await session.execute( + update(Location) + .where(Location.id.in_(request.location_ids)) + .values(release_status='public') + ) + return {"updated": len(request.location_ids)} +``` + +**Staff Usage**: +```bash +# Via curl +curl -X POST http://localhost:8000/admin/bulk-publish \ + -H "Authorization: Bearer $TOKEN" \ + -d '{"location_ids": ["uuid1", "uuid2", "uuid3"]}' + +# Via Python script +import requests +response = requests.post( + "http://localhost:8000/admin/bulk-publish", + json={"location_ids": ["uuid1", "uuid2", "uuid3"]}, + headers={"Authorization": f"Bearer {token}"} +) +``` + +**Verdict**: **Supplement to any admin UI** - useful for automation, not replacement + +--- + +#### ❌ **Custom React Admin (from scratch)** +- **Development Time**: 3-4 months +- **Effort**: 300-400 hours +- **Verdict**: **Not recommended** - too slow for migration timeline + +--- + +### Recommendation: Decision Tree + +``` +START: Do you need admin UI immediately (< 2 weeks)? +├─ YES → No Admin UI (use pgAdmin/psql temporarily) +└─ NO → Continue + +Can staff tolerate deferred features (maps, forms) for 2-4 months? +├─ NO → Keep OcotilloUI (safest, no changes) +└─ YES → Continue + +Do you want Python-only stack (no Node.js)? +├─ YES → Starlette Admin ⭐ RECOMMENDED +└─ NO → Simplify OcotilloUI (strip unused features) + +If Starlette Admin proves insufficient after MVP: +├─ Short-term fix → Restore OcotilloUI for missing features +├─ Long-term solution → Django Admin microservice +└─ Hybrid approach → Starlette Admin + custom React forms +``` + +**Final Recommendation**: **Start with Starlette Admin** for 3 reasons: +1. **Speed**: 2-4 weeks to MVP vs. 6-8 weeks for Django Admin +2. **Low Risk**: Can fall back to OcotilloUI if it fails +3. **Low Lock-in**: Uses your existing SQLAlchemy models + +--- + +## Question 2: Monolithic vs. Separate Repository + +### Can Starlette Admin Live in NMSampleLocations Repo? + +**Short Answer**: ✅ **YES** - Starlette Admin can (and should) live in the NMSampleLocations monolithic repo. + +### Current NMSampleLocations Architecture + +**Repo Structure**: +``` +NMSampleLocations/ +├── main.py # FastAPI entry point +├── core/ +│ ├── app.py # FastAPI app initialization +│ └── initializers.py # Route registration +├── api/ # REST API endpoints +│ ├── location.py +│ ├── sensor.py +│ ├── sample.py +│ └── ... +├── db/ # SQLAlchemy models (50+ models) +│ ├── location.py +│ ├── sensor.py +│ └── ... +├── schemas/ # Pydantic schemas +└── services/ # Business logic +``` + +**Current Pattern**: **Monolithic FastAPI application** +- All API routes registered in `core/initializers.py` +- Single deployment unit +- Shared database session management + +--- + +### Option A: Monolithic (Admin in Same Repo) ⭐ **RECOMMENDED** + +**Structure**: +``` +NMSampleLocations/ +├── main.py # Mount both API and admin +├── core/ +│ ├── app.py +│ └── initializers.py # Register API routes AND admin +├── api/ # REST API endpoints +│ ├── location.py +│ └── ... +├── admin/ # ← NEW: Starlette Admin +│ ├── __init__.py +│ ├── auth.py # Auth provider for admin +│ ├── views.py # Custom ModelViews +│ └── actions.py # Bulk actions (publish, export) +├── db/ # Shared SQLAlchemy models +├── schemas/ # Pydantic schemas (API only) +└── services/ # Shared business logic +``` + +**Updated `core/initializers.py`**: +```python +def register_routes(app): + # Existing API routes + from api.location import router as location_router + from api.sensor import router as sensor_router + # ... (all existing routers) + + app.include_router(location_router) + app.include_router(sensor_router) + # ... + + # NEW: Mount admin interface + from admin import create_admin + admin = create_admin() + admin.mount_to(app) # Mounts at /admin by default +``` + +**New `admin/__init__.py`**: +```python +from starlette_admin.contrib.sqla import Admin, ModelView +from db import Location, Sensor, Sample, Contact, Asset, Group +from db.engine import engine +from .auth import NMBGMRAuthProvider +from .views import ( + LocationAdmin, SensorAdmin, SampleAdmin, + ContactAdmin, AssetAdmin, GroupAdmin +) + +def create_admin(): + admin = Admin( + engine, + title="NMSampleLocations Admin", + base_url="/admin", + auth_provider=NMBGMRAuthProvider() + ) + + # Register model views + admin.add_view(LocationAdmin(Location)) + admin.add_view(SensorAdmin(Sensor)) + admin.add_view(SampleAdmin(Sample)) + admin.add_view(ContactAdmin(Contact)) + admin.add_view(AssetAdmin(Asset)) + admin.add_view(GroupAdmin(Group)) + + return admin +``` + +**Deployment**: +```bash +# Single service +uvicorn main:app --host 0.0.0.0 --port 8000 + +# Routes available: +# - http://localhost:8000/docs → OpenAPI (public) +# - http://localhost:8000/docs-auth → OpenAPI (authenticated) +# - http://localhost:8000/location → REST API +# - http://localhost:8000/admin → Starlette Admin UI ← NEW +``` + +**Docker Compose** (unchanged): +```yaml +services: + app: + build: . + ports: + - "8000:8000" + environment: + - DATABASE_URL=postgresql://... + # Single container runs both API and admin +``` + +--- + +### Option B: Separate Repository (Admin as Microservice) + +**Structure**: +``` +NMSampleLocations/ # Existing FastAPI API +├── api/ +├── db/ +└── ... + +NMSampleLocations-Admin/ # NEW: Separate admin service +├── main.py +├── admin/ +│ ├── views.py +│ └── auth.py +├── db/ # ← DUPLICATED models +│ ├── location.py # Same as NMSampleLocations/db/location.py +│ └── ... +└── requirements.txt +``` + +**Deployment**: +```yaml +services: + api: + build: ./NMSampleLocations + ports: + - "8000:8000" + + admin: + build: ./NMSampleLocations-Admin + ports: + - "8001:8000" + + db: + image: postgis/postgis + # Both services connect to same database +``` + +**Routes**: +- API: `http://localhost:8000/location` +- Admin: `http://localhost:8001/admin` + +--- + +### Comparison: Monolithic vs. Microservice + +| Aspect | Monolithic (Option A) | Microservice (Option B) | +|--------|----------------------|------------------------| +| **Code Duplication** | ✅ No duplication (shared models) | ❌ Models duplicated across repos | +| **Deployment** | ✅ Single service | ⚠️ Two services to deploy | +| **Development** | ✅ Single repo, single environment | ⚠️ Two repos, sync models manually | +| **Authentication** | ✅ Shared auth logic | ⚠️ Auth sync complexity | +| **Database Migrations** | ✅ Single Alembic migration | ⚠️ Must coordinate migrations | +| **Independent Scaling** | ❌ Can't scale admin separately | ✅ Can scale admin independently | +| **Technology Lock-in** | ⚠️ Admin tied to FastAPI | ✅ Can use different framework | +| **CI/CD Complexity** | ✅ Single pipeline | ⚠️ Two pipelines | +| **Monitoring** | ✅ Single service to monitor | ⚠️ Two services to monitor | +| **Ease of Swapping** | ⚠️ Harder to replace admin | ✅ Easy to replace admin service | + +--- + +### Hybrid Option C: Monolithic with Admin Toggle + +**Best of Both Worlds**: Admin lives in same repo but can be **optionally disabled** via environment variable. + +```python +# core/initializers.py +def register_routes(app): + # Always register API routes + from api.location import router as location_router + # ... + app.include_router(location_router) + + # Conditionally mount admin + if settings.ENABLE_ADMIN: # ← Environment variable + from admin import create_admin + admin = create_admin() + admin.mount_to(app) +``` + +**.env**: +```bash +# Production API deployment (no admin) +ENABLE_ADMIN=false + +# Development or admin-only deployment +ENABLE_ADMIN=true +``` + +**Deployment Flexibility**: +```yaml +# Option 1: Combined deployment +services: + app: + environment: + - ENABLE_ADMIN=true # Both API and admin + +# Option 2: Separate deployments (same codebase!) +services: + api: + environment: + - ENABLE_ADMIN=false # API only + + admin: + environment: + - ENABLE_ADMIN=true # Admin only +``` + +**Benefits**: +- ✅ Single codebase, shared models +- ✅ Flexibility to deploy separately later +- ✅ Easy to disable admin in production if needed +- ✅ Can scale admin separately if using separate deployments + +--- + +## Architectural Recommendation + +### For AMPAPI Migration: **Option A (Monolithic)** ⭐ + +**Rationale**: + +1. **Simplicity**: NMSampleLocations is already monolithic - adding admin doesn't change architecture +2. **Speed**: No need to set up separate repo, CI/CD, deployment +3. **Code Sharing**: Admin views use existing SQLAlchemy models, no duplication +4. **Easy Testing**: Test both API and admin in single pytest suite +5. **Migration Focus**: Reduces complexity during critical migration period + +**Implementation Path**: +```bash +# Week 1: Add admin to existing repo +cd NMSampleLocations +mkdir admin +touch admin/__init__.py admin/auth.py admin/views.py + +# Week 2-4: Implement admin views +# (No separate repo, no separate deployment) + +# Production: Single deployment, both API and admin +docker build -t nmsamplelocations . +docker run -p 8000:8000 nmsamplelocations +# → http://localhost:8000/location (API) +# → http://localhost:8000/admin (Admin UI) +``` + +**Future Flexibility**: If you later decide admin needs to be separate: +1. Move `admin/` directory to new repo +2. Copy `db/` models (or use shared package) +3. Deploy as separate service + +**Low lock-in**: Adding admin to monolith doesn't prevent future microservice split. + +--- + +### When to Use Separate Repository (Option B) + +✅ **Use microservice approach if:** + +1. **Admin is long-term** (not temporary for migration) +2. **Different scaling needs** (admin has different load pattern than API) +3. **Different teams** (separate admin team, separate API team) +4. **Different tech stacks** (e.g., Django Admin for admin, FastAPI for API) +5. **Security isolation** (admin on internal network, API public) + +❌ **Don't use microservice if:** +- Admin is **temporary** during migration +- You have **limited DevOps resources** +- You want **fast iteration** (monolith is faster to develop) + +--- + +## Migration Strategy with Monolithic Admin + +### Phase 1: Add Admin to Monolith (Week 1) +```python +# NMSampleLocations/admin/__init__.py +from starlette_admin.contrib.sqla import Admin +from db.engine import engine + +def create_admin(): + admin = Admin(engine, title="NM Sample Locations") + # Start with 3 models for testing + from admin.views import LocationAdmin, SensorAdmin, ContactAdmin + admin.add_view(LocationAdmin) + admin.add_view(SensorAdmin) + admin.add_view(ContactAdmin) + return admin +``` + +### Phase 2: Expand Models (Week 2) +- Add remaining 7 models (10 total) +- Implement auth provider +- Add RBAC permissions + +### Phase 3: Custom Features (Week 3-4) +- Bulk actions +- Export functionality +- Custom dashboards + +### Phase 4: Production (Week 5) +- Deploy single service with both API and admin +- Monitor usage +- Gather feedback + +### Post-Migration: Reevaluate Architecture +**After 3-6 months of usage**, decide: +- ✅ Keep monolithic admin (if working well) +- ↔️ Split into microservice (if scaling/isolation needed) +- ↩️ Restore OcotilloUI (if Starlette Admin insufficient) +- ⏩ Build custom React admin (if long-term investment justified) + +--- + +## Final Recommendations + +### Question 1: Starlette Admin vs. Alternatives? + +**Answer**: ✅ **Starlette Admin is the right choice** for these reasons: + +1. **Speed**: 2-4 weeks vs. 6-8 weeks (Django Admin) or 3-4 months (custom React) +2. **Low Risk**: Can fall back to OcotilloUI if it fails UAT +3. **Python-Only**: No Node.js, simpler stack +4. **Native Integration**: Works directly with your SQLAlchemy models +5. **Temporary Intent**: Perfect for migration period, not over-engineering + +**Alternative to Consider**: Keep OcotilloUI if staff reject Starlette Admin during UAT. + +--- + +### Question 2: Monolithic vs. Separate Repo? + +**Answer**: ✅ **Monolithic (admin in NMSampleLocations repo)** for these reasons: + +1. **Already Monolithic**: NMSampleLocations is single-service architecture +2. **Code Sharing**: No model duplication, single source of truth +3. **Simple Deployment**: Single Docker container, single CI/CD pipeline +4. **Migration Focus**: Reduces complexity during critical period +5. **Future Flexibility**: Can split later if needed (low lock-in) + +**Implementation**: +``` +NMSampleLocations/ +├── api/ # Existing REST API +├── admin/ # ← NEW: Starlette Admin views +├── db/ # Shared SQLAlchemy models +├── core/initializers.py # Register both API routes and admin +└── main.py # Single entry point +``` + +--- + +## Next Steps + +1. ✅ **Approve Starlette Admin** as temporary admin framework +2. ✅ **Confirm monolithic approach** (admin in NMSampleLocations repo) +3. ⏭️ **Build prototype** (Week 1): Add 2-3 models to admin UI +4. ⏭️ **User acceptance testing** (Week 2): Show to staff, gather feedback +5. ⏭️ **Full implementation** (Week 3-4): Add all models, auth, custom actions +6. ⏭️ **Production deployment** (Week 5): Single service with API + admin +7. ⏭️ **Post-migration review** (3-6 months): Keep, replace, or enhance admin + +**Timeline**: **4-5 weeks** from decision to production-ready admin UI. + +**Risk**: **Low** - can keep OcotilloUI running in parallel as safety net. diff --git a/docs/ADMIN_FRAMEWORK_EVALUATION.md b/docs/ADMIN_FRAMEWORK_EVALUATION.md new file mode 100644 index 00000000..49b0128e --- /dev/null +++ b/docs/ADMIN_FRAMEWORK_EVALUATION.md @@ -0,0 +1,310 @@ +# Admin Framework Evaluation for NMSampleLocations + +## Project Context + +**NMSampleLocations** is a production-oriented geospatial sample data management system for the New Mexico Bureau of Geology and Mineral Resources. + +### Current Stack +- **Backend**: FastAPI / Starlette +- **ORM**: SQLAlchemy 2.0.43 +- **Database**: PostgreSQL + PostGIS (geospatial) +- **Auth**: Role-based access control (Admin/Editor/Viewer) +- **Data Model**: 33+ SQLAlchemy models with complex relationships +- **Special Requirements**: + - GeoAlchemy2 for geospatial data + - Data visibility controls (`release_status` field) + - Versioned data (SQLAlchemy-Continuum) + - Complex domain: locations, things, samples, observations, sensors, deployments + +--- + +## Executive Recommendation + +**Primary Choice: Starlette Admin** + +### Rationale + +1. **Perfect ORM Fit**: Native SQLAlchemy support (you already use SQLAlchemy 2.0.43) +2. **Geospatial Ready**: Works with GeoAlchemy2 out of the box +3. **RBAC Built-in**: Matches your existing permission model (Admin/Editor/Viewer) +4. **Production Maturity**: Battle-tested, active maintenance +5. **Extensibility**: Can handle complex workflows and custom business logic +6. **Low Lock-in**: Uses standard SQLAlchemy models, easy to migrate away if needed + +--- + +## Detailed Evaluation for NMSampleLocations + +### Option 1: Starlette Admin ⭐ **RECOMMENDED** + +**Installation** +```bash +pip install starlette-admin[i18n] +# Or with uv: +uv add starlette-admin[i18n] +``` + +**Why It's Perfect for You** + +✅ **SQLAlchemy Integration** +- Works directly with your existing models +- No schema duplication needed +- Supports SQLAlchemy-Continuum (versioned data) + +✅ **Geospatial Support** +```python +from starlette_admin.contrib.sqla import Admin, ModelView +from geoalchemy2 import Geometry + +# Your Location model will work out of the box +class LocationAdmin(ModelView): + fields = ['id', 'point', 'elevation', 'release_status', ...] + # PostGIS geometry fields render properly +``` + +✅ **RBAC Implementation** +```python +from starlette_admin.auth import AdminUser, AuthProvider + +class NMBGMRAuthProvider(AuthProvider): + async def is_authenticated(self, request): + # Your existing auth logic + user = get_current_user(request) + return user is not None + + async def get_admin_user(self, request): + user = get_current_user(request) + return AdminUser( + username=user.name, + roles=user.permissions # ['Admin', 'Editor', 'Viewer'] + ) + +# In your admin views: +class LocationAdmin(ModelView): + def can_create(self, request): + return 'Admin' in request.state.user.roles + + def can_edit(self, request): + return any(role in request.state.user.roles for role in ['Admin', 'Editor']) +``` + +✅ **Data Visibility Controls** +```python +class LocationAdmin(ModelView): + # Override queryset based on user role + async def get_list_query(self, request): + query = select(Location) + + # Staff see all data + if request.state.user: + return query + + # Public see only public data + return query.where(Location.release_status == 'public') +``` + +✅ **Custom Actions** +```python +class LocationAdmin(ModelView): + actions = ['bulk_publish', 'export_geojson'] + + async def bulk_publish(self, request, pks): + # Bulk change release_status to 'public' + await session.execute( + update(Location) + .where(Location.id.in_(pks)) + .values(release_status='public') + ) +``` + +**Example Implementation** +```python +# admin_setup.py +from starlette_admin import CustomView +from starlette_admin.contrib.sqla import Admin, ModelView +from starlette.applications import Starlette + +from db import Location, Thing, Sample, Observation, Sensor +from db.engine import engine + +# Create admin +admin = Admin( + engine, + title="NMSampleLocations Admin", + base_url='/admin', + auth_provider=NMBGMRAuthProvider() +) + +# Register model views +admin.add_view(LocationAdmin(Location)) +admin.add_view(ModelView(Thing)) +admin.add_view(ModelView(Sample)) +admin.add_view(ModelView(Observation)) +admin.add_view(ModelView(Sensor)) + +# Mount to FastAPI +from fastapi import FastAPI +app = FastAPI() +admin.mount_to(app) +``` + +**Estimated Setup Time**: 1-2 days for basic CRUD, 1 week for full customization + +**Pros** +- ✓ Works with all 33+ of your models immediately +- ✓ Handles complex relationships +- ✓ GeoJSON/PostGIS support +- ✓ Customizable per-model permissions +- ✓ Active community + +**Cons** +- ⚠ UI is functional but not flashy (admin-focused, not customer-facing) +- ⚠ Requires custom views for complex workflows + +**Best For** +- Internal staff tool for data management +- RBAC-driven access to all models +- Quick setup with room to grow + +--- + +### Option 2: SQLAlchemy Admin (SQLAdmin) + +**Why Consider It** + +Simpler alternative if you don't need geospatial UI features or complex RBAC. + +✅ **Strengths** +- Lightweight, minimal configuration +- Clean UI +- Works with your SQLAlchemy models + +❌ **Limitations for Your Project** +- Less flexible permission system +- Weaker geospatial rendering +- Limited custom action support + +**Verdict**: Good for simple admin needs, but Starlette Admin is more future-proof for your use case. + +--- + +### Option 3: FastAPI Amis Admin + +**Why It Might Appeal** + +If you want a highly customizable, dashboard-heavy admin with charts and complex forms. + +✅ **Strengths** +- Beautiful, modern UI +- Supports complex dashboards +- Works with SQLAlchemy + +❌ **Limitations for Your Project** +- Steeper learning curve (AMIS schema DSL) +- Overkill for internal tooling +- More maintenance overhead + +**Verdict**: Consider if you need public-facing dashboards or complex visualization. Not recommended for internal-only admin. + +--- + +### ❌ Options to Avoid + +**FastAPI Admin**: Requires Tortoise ORM (you use SQLAlchemy) +**Piccolo Admin**: Requires Piccolo ORM (you use SQLAlchemy) + +--- + +## Comparison Matrix for NMSampleLocations + +| Criteria | Starlette Admin | SQLAdmin | Amis Admin | +|----------|----------------|----------|------------| +| **SQLAlchemy Support** | ✅ Native | ✅ Native | ✅ Native | +| **GeoAlchemy2/PostGIS** | ✅ Built-in | ⚠ Basic | ⚠ Basic | +| **RBAC Implementation** | ✅ Flexible | ⚠ Manual | ✅ Good | +| **Complex Relationships** | ✅ Excellent | ✅ Good | ✅ Good | +| **Custom Actions** | ✅ Easy | ⚠ Limited | ✅ Flexible | +| **Setup Time** | Medium | Fast | Slow | +| **Maintenance Burden** | Low | Very Low | Medium-High | +| **Future-Proof** | ✅ High | ✅ Medium | ⚠ Medium | + +--- + +## Implementation Roadmap + +### Phase 1: Core Setup (Week 1) +1. Install Starlette Admin +2. Create basic ModelViews for top 5 models (Location, Thing, Sample, Observation, Sensor) +3. Implement authentication provider using existing permissions.py +4. Test geospatial field rendering + +### Phase 2: Authorization (Week 2) +1. Implement role-based view filtering +2. Add per-model permission checks (can_create, can_edit, can_delete) +3. Apply data visibility rules (release_status filtering) + +### Phase 3: Custom Actions (Week 3) +1. Bulk publish/unpublish actions +2. GeoJSON export action +3. Data quality validation actions + +### Phase 4: Advanced Features (Week 4+) +1. Custom dashboard views +2. Audit log integration +3. Advanced search/filtering +4. Workflow automation (e.g., provisional → approved status transitions) + +--- + +## Alternatives if Starlette Admin Doesn't Work + +### Plan B: Django Admin (Separate Service) +If you need enterprise-grade features: +- Run Django admin as separate microservice +- Share PostgreSQL database +- Use for admin only, keep FastAPI for API + +**Tradeoff**: Added complexity, but best-in-class admin experience. + +### Plan C: Custom React Admin +If internal team has strong frontend capacity: +- React + Refine.dev or React-Admin +- Full control over UX +- More work upfront, but maximum flexibility + +**Tradeoff**: 2-3x development time, but perfect fit for your needs. + +--- + +## Final Recommendation + +**Start with Starlette Admin.** It's the best fit for: +- Your current tech stack (FastAPI + SQLAlchemy + PostGIS) +- Your permission model (Admin/Editor/Viewer RBAC) +- Your data model complexity (33+ models, geospatial, versioned) +- Your timeline (production-ready admin in 2-4 weeks) + +If requirements evolve beyond CRUD (e.g., complex workflows, customer-facing dashboards), you can: +1. Keep Starlette Admin for internal data management +2. Build custom UI for specific workflows +3. Migrate to Django Admin or custom solution + +**Low risk, high value, production-ready.** + +--- + +## Next Steps + +1. **Prototype**: Spend 1 day building basic Starlette Admin setup for Location model +2. **Evaluate**: Test with real data and your auth system +3. **Decide**: If it works, expand to all models. If not, try SQLAdmin as backup. +4. **Document**: Update this doc with learnings and final choice + +--- + +## Resources + +- [Starlette Admin Documentation](https://jowilf.github.io/starlette-admin/) +- [Starlette Admin + GeoAlchemy2 Example](https://github.com/jowilf/starlette-admin/tree/main/examples/sqla) +- [SQLAlchemy Admin (SQLAdmin)](https://aminalaee.dev/sqladmin/) +- [FastAPI Amis Admin](https://docs.amis.work/) diff --git a/docs/MS_ACCESS_TO_STARLETTE_ADMIN_MIGRATION.md b/docs/MS_ACCESS_TO_STARLETTE_ADMIN_MIGRATION.md new file mode 100644 index 00000000..944756d9 --- /dev/null +++ b/docs/MS_ACCESS_TO_STARLETTE_ADMIN_MIGRATION.md @@ -0,0 +1,582 @@ +# MS Access → Starlette Admin Migration Analysis + +## Critical Context: Staff's Current Admin Interface + +**Legacy System**: MS Access connected to SQL Server (AMPAPI) + +This fundamentally changes our evaluation. Staff are not accustomed to modern web UIs like OcotilloUI - they're accustomed to **MS Access forms and datasheets**. + +--- + +## MS Access vs. Starlette Admin: Feature Comparison + +### What Staff Are Used To (MS Access) + +**MS Access Workflow**: +1. Open Access database file (.accdb or .mdb) +2. Navigate to **Tables** or **Forms** +3. Use **Datasheet View** for list/table operations +4. Use **Form View** for create/edit +5. Run **Queries** for filtering/searching +6. Use **Reports** for exports + +**MS Access Strengths**: +- ✅ Familiar to non-technical users +- ✅ Forms-based data entry +- ✅ Datasheet view for bulk operations +- ✅ Built-in search/filter +- ✅ Direct SQL query access (Power Users) +- ✅ Works offline (local file) + +**MS Access Weaknesses**: +- ❌ Windows-only (no Mac/Linux/web) +- ❌ Single-user concurrency issues (file locking) +- ❌ No audit trail (who changed what) +- ❌ Version control nightmare (binary files) +- ❌ Security issues (file-based permissions) +- ❌ No remote access without VPN/Remote Desktop + +--- + +## Starlette Admin as MS Access Replacement + +### Feature Parity Matrix + +| Feature | MS Access | Starlette Admin | Comparison | +|---------|-----------|----------------|------------| +| **Datasheet View** | ✅ Familiar grid/table view | ✅ List view with sorting/filtering | ✅ **Equivalent** | +| **Form View** | ✅ Custom forms for create/edit | ✅ Auto-generated forms | ✅ **Better** (web-based) | +| **Search/Filter** | ✅ Filter-by-selection, query builder | ✅ Column filters, full-text search | ✅ **Equivalent** | +| **CRUD Operations** | ✅ Create, Read, Update, Delete | ✅ Create, Read, Update, Delete | ✅ **Equivalent** | +| **Relationships** | ✅ Combo boxes for foreign keys | ✅ Dropdown selects for foreign keys | ✅ **Equivalent** | +| **Bulk Operations** | ✅ Datasheet multi-select + action | ✅ Bulk actions (select → action) | ✅ **Equivalent** | +| **Data Validation** | ✅ Form validation rules | ✅ Pydantic/SQLAlchemy validation | ✅ **Better** (enforced in DB) | +| **Export** | ✅ Export to Excel, CSV | ✅ CSV export (built-in) | ✅ **Equivalent** | +| **Reports** | ✅ Access Reports | ❌ No built-in reports | ⚠️ **Missing** (use API + Excel) | +| **Queries** | ✅ Query Designer, SQL view | ⚠️ Admin can filter, devs write SQL | ⚠️ **Partial** | +| **Offline Access** | ✅ Works offline (local file) | ❌ Requires internet connection | ⚠️ **Web-only** | +| **Multi-User** | ⚠️ Limited (file locking issues) | ✅ Full concurrent access | ✅ **Better** | +| **Audit Trail** | ❌ No built-in audit log | ⚠️ Can add custom audit log | ✅ **Better** (if implemented) | +| **Remote Access** | ❌ Requires VPN/RDP | ✅ Web-based, anywhere access | ✅ **Much better** | +| **Cross-Platform** | ❌ Windows only | ✅ Any browser (Mac/Linux/iPad) | ✅ **Much better** | +| **Security** | ⚠️ File-level permissions | ✅ User-based RBAC | ✅ **Better** | +| **UI Polish** | ⚠️ 1990s look and feel | ⚠️ 2010s admin UI | ↔️ **Both functional** | + +--- + +## Key Insights: Why Starlette Admin is a Good Fit + +### 1. **Similar Mental Model** + +**MS Access Users Think In Terms Of:** +- Tables = Lists of records +- Forms = Create/Edit screens +- Datasheets = Browse/filter records +- Queries = Find specific records + +**Starlette Admin Provides:** +- **List View** = Access Datasheet View (grid of records with sorting/filtering) +- **Create/Edit Forms** = Access Form View (auto-generated from model) +- **Detail View** = Access Form View (read-only) +- **Search** = Access Query-by-Form (filter records) + +**Visual Comparison**: + +``` +MS ACCESS DATASHEET VIEW STARLETTE ADMIN LIST VIEW +┌──────────────────────────────────┐ ┌──────────────────────────────────┐ +│ ▼ Location Table │ │ 🔍 Search: ________ │ +│ ┌─────┬─────────┬──────────┬───┐ │ │ ┌─────┬─────────┬──────────┬───┐│ +│ │ ID │ PointID │ Latitude │...│ │ │ │ ID ↑│ PointID │ Latitude │...││ +│ ├─────┼─────────┼──────────┼───┤ │ │ ├─────┼─────────┼──────────┼───┤│ +│ │ 123 │ WELL001 │ 35.1234 │...│ │ │ │ 123 │ WELL001 │ 35.1234 │...││ +│ │ 124 │ WELL002 │ 35.2345 │...│ │ │ │ 124 │ WELL002 │ 35.2345 │...││ +│ └─────┴─────────┴──────────┴───┘ │ │ └─────┴─────────┴──────────┴───┘│ +│ [Filter] [Sort] [New Record] │ │ [1] 2 3 ... Next » [+ Create] │ +└──────────────────────────────────┘ └──────────────────────────────────┘ +``` + +**Staff Won't Feel Lost** - The paradigm is nearly identical. + +--- + +### 2. **UI Expectations Are Low (Good Thing!)** + +**Access Users Are Accustomed To:** +- 🎨 **Plain, utilitarian UI** (gray forms, basic controls) +- 📝 **Function over form** (no flashy animations or modern UX) +- 🖱️ **Click-heavy workflows** (lots of navigation) + +**Starlette Admin Provides:** +- 🎨 **Clean, functional admin UI** (Bootstrap-based, better than Access) +- 📝 **Focus on CRUD efficiency** (same as Access) +- 🖱️ **Straightforward navigation** (sidebar menu, breadcrumbs) + +**Expectation Management**: Staff coming from Access will likely **prefer** Starlette Admin's web UI to Access's dated interface. + +**OcotilloUI comparison**: If staff were using OcotilloUI (modern React UI with Material Design), switching to Starlette Admin would feel like a downgrade. But coming from **MS Access → Starlette Admin feels like an upgrade**. + +--- + +### 3. **Missing Features Staff Might Miss** + +| MS Access Feature | How to Replicate in Starlette Admin | Effort | +|-------------------|-------------------------------------|--------| +| **Complex Queries** | Use FastAPI endpoints + Postman, or staff request custom filters | Medium | +| **Access Reports** | Export CSV → open in Excel → create pivot tables/charts | Easy | +| **Offline Work** | Not possible (web-based), need internet | N/A | +| **Macros/VBA** | Replace with Starlette Admin custom actions | Medium-High | +| **Linked Tables** | Use foreign key relationships (built-in) | Trivial | + +**Most Critical Loss**: **Offline access** (Access works without internet) + +**Mitigation**: +- Web-based admin requires internet connection +- If staff work in field without internet → provide CSV export for offline review +- For data entry in field → use mobile-friendly admin UI (Starlette Admin is responsive) + +--- + +### 4. **Gains Over MS Access** + +| Gain | Impact | Staff Benefit | +|------|--------|---------------| +| **Multi-user concurrency** | High | No more "database is locked" errors | +| **Remote access** | High | Work from home without VPN/RDP | +| **Cross-platform** | Medium | Mac/Linux users can access | +| **Audit trail** | High | Track who changed what when | +| **Better security** | High | User-level permissions, not file permissions | +| **Version control** | Medium | Admin code is in Git, not binary .accdb files | +| **Integration** | High | Same system as REST API (NMSampleLocations) | +| **No corruption** | High | PostgreSQL is more robust than Access .accdb files | + +**Staff Will Appreciate**: No more Access file corruption, no more "compact & repair database", no more file locking issues. + +--- + +## Recommended Migration Path: MS Access → Starlette Admin + +### Phase 0: Document Access Workflows (Week 0) + +**Action**: Catalog what staff **actually do** in MS Access: + +``` +TASK INVENTORY TEMPLATE: +┌─────────────────────────────────────────────────────┐ +│ Task: Add new well location │ +│ Frequency: 3-5 times per week │ +│ Current Tool: MS Access "Location Entry Form" │ +│ Steps: │ +│ 1. Open Access DB │ +│ 2. Navigate to "Location Entry Form" │ +│ 3. Click "New Record" │ +│ 4. Fill in fields (PointID, Lat/Lon, etc.) │ +│ 5. Select Owner from dropdown │ +│ 6. Save record │ +│ Starlette Equivalent: │ +│ 1. Navigate to /admin/location │ +│ 2. Click "Create" │ +│ 3. Fill in fields │ +│ 4. Select Owner from dropdown │ +│ 5. Click "Save" │ +│ Migration Effort: Trivial (1:1 mapping) │ +└─────────────────────────────────────────────────────┘ +``` + +**Deliverable**: Spreadsheet of: +- Task name +- Frequency (daily/weekly/monthly/rare) +- Current Access workflow (steps) +- Missing features in Starlette Admin (if any) +- Workaround or custom implementation needed + +--- + +### Phase 1: Prototype with 3 Most-Used Tables (Week 1) + +**Goal**: Build admin for staff's **top 3 most-used tables** from Access. + +**Example** (adjust based on your Access DB): +1. **Location** (most frequent CRUD operations) +2. **Well** (second most frequent) +3. **WaterLevel** (third most frequent) + +**Implementation**: +```python +# admin/views.py +from starlette_admin.contrib.sqla import ModelView + +class LocationAdmin(ModelView): + model = Location + name = "Locations" + icon = "fa fa-map-marker" + + # Match Access datasheet column order + column_list = ['id', 'point_id', 'latitude', 'longitude', 'elevation'] + + # Match Access form field order + fields = ['point_id', 'latitude', 'longitude', 'elevation', 'county', 'owner'] + + # Enable search (like Access filter-by-selection) + search_fields = ['point_id', 'county'] + +# Similar for Well, WaterLevel +``` + +**UAT**: Have 2-3 staff members test prototype: +- Can they create a new record? +- Can they find and edit existing records? +- Can they export to CSV? +- What feels confusing vs. Access? + +--- + +### Phase 2: Add Remaining Tables (Week 2) + +Based on Access database schema, add all remaining tables to Starlette Admin. + +**Auto-generation**: Starlette Admin can auto-generate basic views for all SQLAlchemy models: + +```python +# Quick approach: Auto-add all models +from db import ( + Location, Well, Spring, Sample, Sensor, + Contact, Owner, WaterLevel, WaterQuality, # ... +) + +models = [Location, Well, Spring, Sample, Sensor, ...] + +for model in models: + admin.add_view(ModelView(model)) +``` + +**Then customize** the 5-10 most frequently used tables with: +- Custom column ordering +- Search fields +- Filters +- Custom form layouts + +--- + +### Phase 3: Replicate Access Forms (Week 3) + +**Access Forms → Starlette Admin Custom Forms** + +**Example: MS Access "Well Entry Form" (complex multi-section form)** + +``` +MS ACCESS FORM STARLETTE ADMIN EQUIVALENT +┌────────────────────────────┐ ┌────────────────────────────┐ +│ Well Information │ │ Create Well │ +│ ┌────────────────────────┐ │ │ ┌────────────────────────┐ │ +│ │ PointID: [WELL001 ] │ │ │ │ PointID: [WELL001 ] │ │ +│ │ Latitude: [35.1234 ] │ │ │ │ Latitude: [35.1234 ] │ │ +│ │ Longitude: [-106.123 ] │ │ │ │ Longitude: [-106.123 ] │ │ +│ └────────────────────────┘ │ │ └────────────────────────┘ │ +│ Well Construction │ │ Well Construction │ +│ ┌────────────────────────┐ │ │ ┌────────────────────────┐ │ +│ │ Depth: [150 ] │ │ │ │ Depth: [150 ] │ │ +│ │ Casing: [6 inch ] │ │ │ │ Casing: [6 inch ] │ │ +│ └────────────────────────┘ │ │ └────────────────────────┘ │ +│ [Save] [Cancel] │ │ [Save] [Cancel] │ +└────────────────────────────┘ └────────────────────────────┘ +``` + +**Implementation**: +```python +from starlette_admin import fields + +class WellAdmin(ModelView): + # Group fields into sections (like Access form sections) + fields = [ + fields.StringField('point_id', label='PointID'), + fields.DecimalField('latitude'), + fields.DecimalField('longitude'), + '---', # Separator + fields.IntegerField('depth_ft', label='Depth (ft)'), + fields.StringField('casing_diameter', label='Casing'), + ] +``` + +**For Complex Forms**: If Access form has custom VBA logic, implement as custom Starlette Admin view or FastAPI endpoint. + +--- + +### Phase 4: Replicate Access Queries (Week 3-4) + +**MS Access Saved Queries → Starlette Admin Filters or Custom Views** + +**Example: Access Query "Wells in Bernalillo County"** + +```sql +-- Access Query (SQL View) +SELECT * FROM Wells +WHERE County = 'Bernalillo' +ORDER BY PointID; +``` + +**Starlette Admin Equivalent**: + +**Option A: Built-in Filters** +```python +class WellAdmin(ModelView): + # Add county as filterable column + column_filters = ['county', 'depth_ft', 'drill_date'] +``` + +Staff can then: +1. Go to Wells list view +2. Click "Filter" button +3. Select "County = Bernalillo" +4. Results show filtered list + +**Option B: Custom Dashboard Widget** +```python +from starlette_admin import CustomView + +class DashboardView(CustomView): + async def render(self, request): + # Query for Bernalillo wells + wells = session.query(Well).filter_by(county='Bernalillo').all() + return self.templates.TemplateResponse( + 'dashboard.html', + {'wells': wells} + ) + +admin.add_view(DashboardView(name="Dashboard")) +``` + +**Option C: Saved Filters (Future Enhancement)** +Could implement "saved search" feature similar to Access saved queries. + +--- + +### Phase 5: Replicate Access Reports (Week 4 - Optional) + +**MS Access Reports → CSV Export + Excel Pivot Tables** + +**Access Reports Are Used For**: +1. Summary reports (e.g., "Wells by County") +2. Printable forms (e.g., "Well Construction Record") +3. Data exports (e.g., "All water levels for 2024") + +**Starlette Admin Approach**: + +**Simple Reports** → CSV Export +```python +class WellAdmin(ModelView): + # Built-in CSV export + can_export = True # Adds "Export to CSV" button +``` + +Staff workflow: +1. Filter wells (e.g., county = Bernalillo) +2. Click "Export to CSV" +3. Open in Excel +4. Create pivot table or chart + +**Complex Reports** → FastAPI Endpoint + Excel Template +```python +# api/reports.py +@router.get("/reports/wells-by-county") +async def wells_by_county_report(): + """Generate Excel report of wells grouped by county.""" + # Use openpyxl or xlsxwriter to generate .xlsx file + return FileResponse("wells_by_county.xlsx") +``` + +**Printable Forms** → HTML/PDF Generation +```python +from starlette_admin import action + +class WellAdmin(ModelView): + @action( + name="print_construction_record", + text="Print Construction Record", + ) + async def print_construction_record(self, request, pks): + # Generate PDF from template + pass +``` + +--- + +## Staff Training: MS Access → Starlette Admin + +### Training Session 1: Core Concepts (1 hour) + +**Cover**: +1. **What changed**: Desktop app → Web app +2. **What stayed the same**: Tables, Forms, CRUD operations +3. **Key improvements**: Multi-user, remote access, no file corruption + +**Demo**: +- Access Datasheet View → Starlette List View (side-by-side comparison) +- Access Form View → Starlette Create/Edit Form +- Access Filter-by-Selection → Starlette Column Filters + +--- + +### Training Session 2: Common Tasks (1 hour) + +**Hands-on exercises**: + +| Task | MS Access Steps | Starlette Admin Steps | +|------|----------------|----------------------| +| **Add new well** | Open DB → Forms → Well Entry → New | Login → Wells → Create | +| **Find well by ID** | Open Wells table → Ctrl+F → Search | Go to Wells → Search box | +| **Edit existing record** | Find record → Click → Edit | Find record → Click row → Edit | +| **Export to Excel** | File → Export → Excel | Select records → Export → CSV | +| **Filter by county** | Query Designer → Run query | Wells → Filter → County | + +--- + +### Training Session 3: Differences & Workarounds (30 min) + +**What's Different**: + +| Access Feature | Starlette Admin | Workaround | +|---------------|----------------|------------| +| **Offline work** | ❌ Requires internet | Export CSV for offline review | +| **Custom reports** | ❌ No built-in report designer | Export CSV → Excel pivot tables | +| **Complex queries** | ⚠️ Limited query builder | Contact admin to add custom filters | +| **VBA macros** | ❌ No scripting | Request custom actions from developers | + +--- + +## Risk Assessment: MS Access → Starlette Admin + +| Risk | Likelihood | Impact | Mitigation | +|------|-----------|--------|------------| +| **Staff reject web UI** | Low | High | UAT before full rollout; keep Access read-only as fallback | +| **Missing critical Access feature** | Medium | Medium | Document Access workflows upfront; implement workarounds | +| **Internet dependency** | Low | Medium | Ensure reliable internet; provide CSV exports for offline | +| **Learning curve** | Medium | Low | Staff are data-savvy (used Access), web UI is easier | +| **Lost custom Access forms/macros** | High | Medium | Catalog all Access objects; prioritize most-used | + +**Overall Risk**: **Low-Medium** + +**Why Low Risk**: +- Staff already understand database concepts (tables, forms, queries) +- Starlette Admin is **simpler** than Access in many ways (no VBA, no query designer) +- Web UI is **more intuitive** than Access for basic CRUD + +**Key Risk**: Custom Access VBA macros or complex reports that can't be easily replicated. + +**Mitigation**: Catalog all Access macros/reports during Phase 0; prioritize top 10 for custom implementation. + +--- + +## Decision Matrix: MS Access Context Changes Everything + +### With MS Access Context, Should You Use Starlette Admin? + +**✅ YES - Even Stronger Case Now** + +| Factor | Without MS Access Context | With MS Access Context | +|--------|--------------------------|----------------------| +| **UI Expectations** | "Downgrade from OcotilloUI's modern React UI" | **"Upgrade from Access's 1990s UI"** | +| **Feature Parity** | "Missing maps, forms, apps" | **"Has everything Access has (tables, forms, search)"** | +| **Learning Curve** | "New paradigm for staff" | **"Familiar paradigm (tables → forms)"** | +| **Staff Adoption** | "May resist change" | **"Will welcome web-based improvement"** | +| **Implementation Effort** | "Need to match OcotilloUI features" | **"Just need CRUD + basic filters"** | + +**Conclusion**: MS Access users will **prefer Starlette Admin** to Access because: +1. ✅ Web-based (no more file corruption, no more .accdb issues) +2. ✅ Multi-user (no more "database locked" errors) +3. ✅ Remote access (work from anywhere) +4. ✅ Better security (user-level permissions) +5. ✅ Modern UI (still functional, but nicer than Access) + +--- + +## Updated Recommendation + +### Primary Recommendation: Starlette Admin (Monolithic) + +**Confidence Level**: **High** (was Medium, now High with Access context) + +**Why Confidence Increased**: +- Staff mental model aligns perfectly with Starlette Admin +- Staff will see this as an **upgrade**, not a lateral move +- Lower UI expectations make Starlette Admin more than sufficient +- Access features map 1:1 to Starlette Admin capabilities + +**Implementation Priority**: +1. **Week 1**: Prototype with 3 most-used Access tables +2. **Week 2**: Staff UAT (critical - get buy-in early) +3. **Week 3**: Add all tables, replicate Access forms +4. **Week 4**: Custom actions, bulk operations, exports +5. **Week 5**: Production rollout, parallel with Access (read-only) + +**Parallel Operation Period**: 1 month +- Keep Access database **read-only** (for reference/reports) +- All new data entry in Starlette Admin +- Staff can verify data in Access if needed + +**After 1 Month**: Retire Access completely (archive .accdb file) + +--- + +## Next Steps + +1. **✅ Approve Starlette Admin** (even stronger case with MS Access context) +2. **⏭️ Catalog Access workflows** (1-2 days) + - List all Access forms, queries, reports + - Identify top 10 most-used features + - Document any custom VBA macros +3. **⏭️ Build prototype** (Week 1) + - 3 most-used tables + - Show to staff for feedback +4. **⏭️ Staff UAT** (Week 2) + - Demo prototype side-by-side with Access + - Gather feedback on missing features +5. **⏭️ Full implementation** (Week 3-5) +6. **⏭️ Parallel operation** (1 month) +7. **⏭️ Retire Access** (archive for reference) + +**Timeline**: **5-6 weeks** from approval to full Starlette Admin deployment. + +**Success Criteria**: Staff complete 95% of daily tasks in Starlette Admin without needing Access. + +--- + +## Appendix: MS Access Database Inventory Template + +Use this template to document what exists in the legacy Access database: + +### Tables +| Table Name | Record Count | Primary Use | Migration Priority | +|------------|-------------|-------------|-------------------| +| Location | 1,234 | Well/spring locations | P0 - Critical | +| WaterLevel | 45,678 | Water level measurements | P0 - Critical | +| Owner | 156 | Property owners | P1 - High | +| ... | ... | ... | ... | + +### Forms +| Form Name | Frequency of Use | Complexity | Migration Plan | +|-----------|-----------------|-----------|----------------| +| Location Entry | Daily | Simple | Starlette auto-form | +| Well Inventory | Weekly | Complex (3 sections) | Custom Starlette view | +| ... | ... | ... | ... | + +### Queries +| Query Name | Use Case | Migration Plan | +|------------|----------|----------------| +| Wells in Bernalillo | Monthly report | Built-in filter | +| Depth > 200 ft | Ad-hoc analysis | Built-in filter | +| Custom complex query | Annual report | FastAPI endpoint | + +### Reports +| Report Name | Output Format | Migration Plan | +|-------------|---------------|----------------| +| Well Summary | Printed PDF | CSV export → Excel | +| Annual Water Levels | Excel spreadsheet | FastAPI endpoint | +| ... | ... | ... | + +### Macros/VBA +| Macro Name | Functionality | Migration Plan | +|------------|--------------|----------------| +| AutoPublish | Bulk update release_status | Starlette bulk action | +| ValidateCoordinates | Check lat/lon in NM | Pydantic validator | +| ... | ... | ... | diff --git a/docs/OCOTILLOUI_STARLETTE_ADMIN_PARITY.md b/docs/OCOTILLOUI_STARLETTE_ADMIN_PARITY.md new file mode 100644 index 00000000..2b9c33a5 --- /dev/null +++ b/docs/OCOTILLOUI_STARLETTE_ADMIN_PARITY.md @@ -0,0 +1,583 @@ +# OcotilloUI → Starlette Admin Feature Parity Analysis + +## Executive Summary + +This document analyzes the feature gap between **OcotilloUI** (React + Refine.dev admin dashboard) and **Starlette Admin** (Python-based admin framework) for NMSampleLocations during the migration period from AMPAPI. + +**Purpose**: Determine the minimum viable feature set required for Starlette Admin to temporarily replace OcotilloUI during the AMPAPI → NMSampleLocations database migration. + +**Key Finding**: ~80% of OcotilloUI features are standard CRUD operations that Starlette Admin auto-generates. The remaining ~20% require custom implementation (map views, specialized forms, batch upload). + +--- + +## OcotilloUI Current Feature Inventory + +### Core Tech Stack + +**Frontend:** +- React 18 + TypeScript +- Refine.dev 4.x (admin framework) +- Material UI 6.x (component library) +- Mapbox GL JS 3.x (mapping) +- TanStack Query (data fetching) +- React Hook Form + Zod (form validation) + +**Backend API:** +- NMSampleLocations FastAPI (PostgreSQL + PostGIS) +- RESTful endpoints +- OpenAPI schema generation + +**Authentication:** +- Authentik (OAuth/OIDC provider) +- Role-based access control + +--- + +## Resource-by-Resource Comparison + +### 1. Core Tables (Standard CRUD) + +| Resource | OcotilloUI Pages | Starlette Admin Equivalent | Implementation Effort | +|----------|------------------|---------------------------|----------------------| +| **Asset** | List, Create, Edit, Show | `ModelView(Asset)` | ✅ **Trivial** - auto-generated | +| **Contact** | List, Create, Edit, Show | `ModelView(Contact)` | ✅ **Trivial** - auto-generated | +| **Location** | List, Create, Edit, Show | `ModelView(Location)` | ✅ **Trivial** - auto-generated | +| **Sensor** | List, Create, Edit, Show | `ModelView(Sensor)` | ✅ **Trivial** - auto-generated | +| **Sample** | List, Create, Edit, Show | `ModelView(Sample)` | ✅ **Trivial** - auto-generated | +| **Group** | List, Create, Edit, Show | `ModelView(Group)` | ✅ **Trivial** - auto-generated | + +**Notes:** +- All six resources have identical CRUD patterns +- Starlette Admin auto-generates list views with pagination, search, sorting +- Form fields are auto-generated from SQLAlchemy models +- Relationships are handled automatically (foreign key dropdowns) + +**Example Implementation:** +```python +# admin_setup.py +from starlette_admin.contrib.sqla import Admin, ModelView +from db import Asset, Contact, Location, Sensor, Sample, Group + +admin = Admin(engine, title="NMSampleLocations Admin") + +# Auto-generates full CRUD for each model +admin.add_view(ModelView(Asset)) +admin.add_view(ModelView(Contact)) +admin.add_view(ModelView(Location)) +admin.add_view(ModelView(Sensor)) +admin.add_view(ModelView(Sample)) +admin.add_view(ModelView(Group)) +``` + +**Effort**: **1-2 hours** total for all six resources + +--- + +### 2. Thing Resources (Nested Models) + +| Resource | OcotilloUI Implementation | Starlette Admin Approach | Effort | +|----------|---------------------------|-------------------------|--------| +| **Well** | Dedicated pages (List, Create, Edit, Show) | `ModelView(ThingWell)` with custom filters | ⚠️ **Easy** - minor customization | +| **Spring** | Dedicated pages (List, Create, Show) | `ModelView(ThingSpring)` with custom filters | ⚠️ **Easy** - minor customization | +| **Thing-ID-Link** | List, Create, Edit, Show | `ModelView(ThingIdLink)` | ✅ **Trivial** - auto-generated | +| **Well-Screen** | List, Create, Edit, Show | `ModelView(WellScreen)` | ✅ **Trivial** - auto-generated | + +**Challenge**: OcotilloUI treats Wells and Springs as separate top-level resources, but they're actually subtypes of the polymorphic `Thing` model. + +**Starlette Admin Solution:** +```python +class WellAdmin(ModelView): + model = ThingWell + name = "Wells" + icon = "fa fa-tint" + + # Filter to show only wells + def get_list_query(self, request): + return select(ThingWell).where(Thing.thing_type == 'well') + + # Custom form to handle well-specific fields + fields = [ + 'name', 'description', 'location_id', + 'well_depth', 'casing_diameter', 'drill_date', # well-specific + ] + +class SpringAdmin(ModelView): + model = ThingSpring + name = "Springs" + + def get_list_query(self, request): + return select(ThingSpring).where(Thing.thing_type == 'spring') +``` + +**Effort**: **4-6 hours** (handling polymorphic models, custom filtering) + +--- + +### 3. Observations (Time-Series Data) + +| Resource | OcotilloUI Pages | Starlette Admin Approach | Effort | +|----------|------------------|-------------------------|--------| +| **Groundwater Level Observation** | List, Create (no Edit/Show) | `ModelView(GroundwaterLevelObservation)` | ⚠️ **Easy** - basic CRUD | + +**Notes:** +- OcotilloUI only implements List + Create (observations are typically immutable) +- Starlette Admin can replicate this pattern with permission restrictions + +**Implementation:** +```python +class GroundwaterLevelObservationAdmin(ModelView): + model = GroundwaterLevelObservation + name = "Groundwater Levels" + + # Allow create but not edit (observations are immutable) + can_edit = False + can_delete = False # Or only for admins + + # Custom list display + column_list = ['observation_date', 'water_level_value', 'sensor', 'location'] + column_sortable_list = ['observation_date', 'water_level_value'] + column_default_sort = ('observation_date', True) # descending +``` + +**Effort**: **2-3 hours** + +--- + +### 4. Lexicon (Controlled Vocabulary) + +| Resource | OcotilloUI Implementation | Starlette Admin Approach | Effort | +|----------|---------------------------|-------------------------|--------| +| **Lexicon** | Hierarchical list (Categories → Terms) | Two `ModelView`s (Category, Term) | ⚠️ **Medium** - custom UI | +| **Term** | Create, Edit (nested under category) | `ModelView(LexiconTerm)` | ⚠️ **Easy** | +| **Category** | Create, Edit | `ModelView(LexiconCategory)` | ⚠️ **Easy** | + +**OcotilloUI Pattern:** +- Single "Lexicon" page showing all categories +- Expandable/collapsible categories +- Inline term creation + +**Starlette Admin Pattern:** +- Separate views for Categories and Terms +- Category dropdown in Term form (foreign key) +- Less hierarchical UI, more traditional CRUD + +**Trade-off**: Starlette Admin won't replicate the nested expandable UI, but will provide functional CRUD. + +```python +admin.add_view(ModelView(LexiconCategory, name="Lexicon Categories")) +admin.add_view(ModelView(LexiconTerm, name="Lexicon Terms")) +``` + +**Effort**: **2-3 hours** (acceptable UX difference) + +--- + +### 5. Custom Forms (Complex Workflows) + +| Form | OcotilloUI Implementation | Starlette Admin Feasibility | Recommendation | +|------|---------------------------|----------------------------|----------------| +| **Well Inventory Form** | Multi-step wizard (stepper UI), Mapbox geocoding, owner search dialog | ❌ **Not feasible** in Starlette Admin | **Defer** - keep as API endpoint, add later | +| **Groundwater Level Form** | Multi-step wizard, well selection, batch entry | ❌ **Not feasible** in Starlette Admin | **Defer** - use direct model CRUD instead | + +**Analysis:** +These forms are **specialized data entry workflows** with: +- Multi-step wizards +- Real-time geocoding and validation +- Complex cross-model validation +- Custom UX optimizations + +**Migration Strategy:** +1. **Phase 1 (Immediate)**: Skip custom forms, use direct CRUD on underlying models + - Staff can create Wells directly via Well CRUD + - Staff can create Observations directly via Observation CRUD +2. **Phase 2 (Post-Migration)**: Re-implement forms as: + - **Option A**: Custom Starlette Admin views (more work) + - **Option B**: Separate React mini-app mounted alongside Starlette Admin + - **Option C**: Restore OcotilloUI for forms only + +**Effort**: **N/A** (out of scope for MVP) + +--- + +### 6. Apps (Specialized Tools) + +| App | OcotilloUI Implementation | Starlette Admin Feasibility | Recommendation | +|-----|---------------------------|----------------------------|----------------| +| **Hydrograph Corrector** | Interactive chart editing (ECharts), data correction UI | ❌ **Not feasible** | **Defer** - "Coming Soon" status in OcotilloUI | +| **Water Chemistry Import** | CSV upload, validation, preview, batch import | ⚠️ **Possible** with custom view | **Defer** or implement as custom action | + +**Analysis:** +- **Hydrograph Corrector**: Currently "coming soon" in OcotilloUI, can remain deferred +- **Water Chemistry Import**: Also "coming soon", could be implemented as Starlette Admin custom action + +**CSV Import Pattern (if needed):** +```python +from starlette_admin import action + +class SampleAdmin(ModelView): + @action( + name="import_csv", + text="Import CSV", + confirmation="Upload and import samples from CSV?", + ) + async def import_csv(self, request, pks): + # Custom file upload and import logic + pass +``` + +**Effort**: **8-12 hours** per app if implemented; **0 hours** if deferred + +--- + +### 7. Map View (Geospatial Visualization) + +| Feature | OcotilloUI Implementation | Starlette Admin Feasibility | Recommendation | +|---------|---------------------------|----------------------------|----------------| +| **Map View** | Mapbox GL JS, location markers, popups, spatial search | ❌ **Not feasible** natively | **Defer** or custom view | + +**Analysis:** +OcotilloUI's map view provides: +- Interactive Mapbox map showing all locations +- Popup details on click +- Spatial search (draw polygon, query locations) +- Basemap selection + +**Starlette Admin Limitations:** +- No built-in mapping UI +- Can display WKT/coordinates in forms, but not interactive maps + +**Options:** +1. **Defer**: Staff can view coordinates in Location list view (lat/lon columns) +2. **Custom View**: Create custom Starlette Admin page with embedded Mapbox map +3. **External Tool**: Use QGIS or PostGIS queries for spatial analysis + +**Effort**: +- Option 1: **0 hours** (acceptable for MVP) +- Option 2: **16-20 hours** (full custom view) +- Option 3: **N/A** (use existing tools) + +--- + +## Feature Parity Matrix + +| Feature Category | OcotilloUI Capability | Starlette Admin Capability | Gap | Priority | +|------------------|----------------------|---------------------------|-----|----------| +| **CRUD Operations** | ✅ Full CRUD for 10+ models | ✅ Auto-generated CRUD | ✅ **No gap** | **P0** | +| **Authentication** | ✅ Authentik OAuth | ✅ Custom auth provider | ✅ **No gap** (integration needed) | **P0** | +| **RBAC Permissions** | ✅ Role-based resource access | ✅ Can/Create/Edit/Delete per model | ✅ **No gap** | **P0** | +| **Search/Filter** | ✅ Full-text search, column filters | ✅ Built-in search and filters | ✅ **No gap** | **P0** | +| **Pagination** | ✅ DataGrid pagination | ✅ Built-in pagination | ✅ **No gap** | **P0** | +| **Relationships** | ✅ Foreign key dropdowns | ✅ Auto-generated selects | ✅ **No gap** | **P0** | +| **Data Validation** | ✅ Zod schemas | ✅ Pydantic/SQLAlchemy validation | ✅ **No gap** (server-side) | **P0** | +| **Map Visualization** | ✅ Interactive Mapbox maps | ❌ Not supported | ⚠️ **Major gap** | **P2** (defer) | +| **Multi-Step Forms** | ✅ Well Inventory, GW Level wizards | ❌ Not supported | ⚠️ **Major gap** | **P2** (defer) | +| **CSV Import/Export** | ✅ Chemistry import app | ⚠️ Possible via custom actions | ⚠️ **Moderate gap** | **P2** (defer) | +| **Chart Editing** | ✅ Hydrograph corrector | ❌ Not supported | ⚠️ **Major gap** | **P3** (coming soon) | +| **Batch Operations** | ⚠️ Limited | ✅ Built-in bulk actions | ✅ **Better in SA** | **P1** | +| **Audit Logging** | ❌ Not implemented | ⚠️ Possible via custom logic | ⚠️ **No gap** (neither has it) | **P3** | + +**Legend:** +- **P0**: Critical for MVP (must have) +- **P1**: High value (should have) +- **P2**: Nice to have (can defer) +- **P3**: Future enhancement + +--- + +## MVP Feature Set for Starlette Admin + +### Phase 1: Core CRUD (Week 1) - **P0** + +**Goal**: Replace OcotilloUI for basic data management + +**Deliverables:** +1. ✅ Authentication integration (existing NMSampleLocations auth) +2. ✅ CRUD for 10 core models: + - Asset, Contact, Location, Sensor, Sample, Group + - ThingWell, ThingSpring, ThingIdLink, WellScreen +3. ✅ Groundwater Level Observation (list + create only) +4. ✅ Lexicon (categories + terms as separate views) + +**Estimated Effort**: **8-12 hours** + +**Acceptance Criteria:** +- [ ] Staff can log in with existing credentials +- [ ] All 10 models have list, create, edit, show pages +- [ ] Relationships render as dropdowns (e.g., Sample → Location) +- [ ] Search and pagination work on all list views +- [ ] Data validation prevents invalid entries + +--- + +### Phase 2: Authorization & Data Visibility (Week 2) - **P0** + +**Goal**: Match OcotilloUI's permission model + +**Deliverables:** +1. ✅ Role-based access control (Admin, Editor, Viewer) + - Admin: Full CRUD on all models + - Editor: Create/Edit/View, no delete + - Viewer: Read-only access +2. ✅ Data visibility controls (`release_status` field) + - Public-released data visible to all + - Provisional/internal data visible to staff only +3. ✅ Per-model permission overrides + +**Implementation:** +```python +from starlette_admin.auth import AuthProvider + +class NMBGMRAuthProvider(AuthProvider): + async def get_admin_user(self, request): + user = get_current_user(request) + return AdminUser( + username=user.name, + roles=user.permissions # ['admin', 'editor', 'viewer'] + ) + +class LocationAdmin(ModelView): + def can_delete(self, request): + return 'admin' in request.state.user.roles + + def can_edit(self, request): + return 'admin' in request.state.user.roles or 'editor' in request.state.user.roles +``` + +**Estimated Effort**: **6-8 hours** + +**Acceptance Criteria:** +- [ ] Viewers cannot create/edit/delete any records +- [ ] Editors can create/edit but not delete +- [ ] Admins have full access +- [ ] Non-authenticated users see only public-released data + +--- + +### Phase 3: Custom Actions (Week 3) - **P1** + +**Goal**: Add bulk operations and export functionality + +**Deliverables:** +1. ✅ Bulk publish/unpublish (change `release_status`) +2. ✅ GeoJSON export for locations +3. ✅ CSV export for all models + +**Implementation:** +```python +class LocationAdmin(ModelView): + actions = ['bulk_publish', 'export_geojson'] + + @action( + name="bulk_publish", + text="Publish Selected", + confirmation="Make selected locations public?", + ) + async def bulk_publish(self, request, pks): + await session.execute( + update(Location) + .where(Location.id.in_(pks)) + .values(release_status='public') + ) + + @action( + name="export_geojson", + text="Export GeoJSON", + ) + async def export_geojson(self, request, pks): + # Generate GeoJSON FeatureCollection + pass +``` + +**Estimated Effort**: **8-10 hours** + +--- + +### Phase 4: Polish & Optimization (Week 4) - **P1/P2** + +**Deliverables:** +1. ⚠️ Custom dashboard (summary stats, recent activity) +2. ⚠️ Advanced search (full-text across all models) +3. ⚠️ Custom list columns (show related data) +4. ⚠️ Improved geospatial field rendering (show coordinates) + +**Estimated Effort**: **10-12 hours** + +--- + +## Deferred Features (Post-Migration) + +### Not Included in MVP + +| Feature | Reason for Deferral | Future Plan | +|---------|---------------------|------------| +| **Map View** | Complex custom UI required | Option 1: Custom Starlette view
Option 2: Keep OcotilloUI for maps only
Option 3: Use QGIS | +| **Well Inventory Form** | Multi-step wizard not in SA core | Re-implement as custom form or restore OcotilloUI | +| **Groundwater Level Form** | Multi-step wizard not in SA core | Use direct Observation CRUD for now | +| **Hydrograph Corrector** | Already "coming soon" in OcotilloUI | Build as separate tool when needed | +| **Water Chemistry Import** | Already "coming soon" in OcotilloUI | Implement as SA custom action later | + +**Rationale**: These features represent **~20% of usage** but **~80% of implementation effort**. Deferring allows for faster migration and validation of core functionality. + +--- + +## Implementation Roadmap + +### Week 1: Core CRUD Setup +- **Day 1-2**: Install Starlette Admin, create basic setup, test with 2-3 models +- **Day 3-4**: Add all 10 core models, test CRUD operations +- **Day 5**: QA testing, bug fixes + +**Milestone**: Staff can perform basic CRUD on all models + +--- + +### Week 2: Authorization +- **Day 1-2**: Implement auth provider integration +- **Day 3**: Add role-based permissions +- **Day 4**: Add data visibility controls +- **Day 5**: QA testing with different user roles + +**Milestone**: Permission model matches OcotilloUI + +--- + +### Week 3: Custom Actions +- **Day 1-2**: Bulk publish/unpublish actions +- **Day 3**: GeoJSON export +- **Day 4**: CSV export +- **Day 5**: QA testing, documentation + +**Milestone**: Staff can perform bulk operations + +--- + +### Week 4: Polish & Launch +- **Day 1-3**: Dashboard, advanced search, custom columns +- **Day 4**: User acceptance testing +- **Day 5**: Production deployment, monitor for issues + +**Milestone**: Starlette Admin replaces OcotilloUI for core workflows + +--- + +## Success Criteria + +### MVP Launch Checklist + +- [ ] All 10 core models have full CRUD functionality +- [ ] Authentication works with existing NMSampleLocations users +- [ ] Role-based permissions match OcotilloUI +- [ ] Data visibility controls enforce public/provisional status +- [ ] Search, filter, pagination work correctly +- [ ] Foreign key relationships render as dropdowns +- [ ] Bulk publish/unpublish actions work +- [ ] CSV export works for all models +- [ ] GeoJSON export works for locations +- [ ] No data loss during migration +- [ ] Performance is acceptable (< 2s page load) + +### Post-Launch Metrics + +**Measure after 2 weeks of use:** +- User adoption rate (% of staff using Starlette Admin vs. OcotilloUI) +- User satisfaction score (survey) +- Bug reports per week +- Feature requests vs. deferred features list + +--- + +## Risk Assessment + +| Risk | Impact | Likelihood | Mitigation | +|------|--------|-----------|------------| +| **Staff reject Starlette Admin UX** | High | Medium | Conduct UAT before full cutover; keep OcotilloUI available as backup | +| **Missing critical workflow** | High | Low | Identify all workflows upfront; defer non-critical features | +| **Performance issues with large datasets** | Medium | Low | Add pagination, indexing; test with production data volume | +| **Auth integration complexity** | Medium | Medium | Test auth early; use existing NMSampleLocations auth code | +| **Geospatial field rendering issues** | Low | Medium | Acceptable to show WKT/coords as text for MVP | + +--- + +## Decision: Go/No-Go for Starlette Admin + +### ✅ Proceed with Starlette Admin If: +- Core CRUD needs are met (10 models) +- Staff can tolerate deferred features (maps, forms) for 2-4 weeks +- MVP can be delivered in 2-3 weeks + +### ❌ Use Alternative If: +- Map view is non-negotiable for daily work → **Keep OcotilloUI for maps** +- Well Inventory Form is critical path → **Keep OcotilloUI or build custom form** +- Staff need all current features → **Keep OcotilloUI, revisit after migration** + +--- + +## Recommendation + +**Proceed with Starlette Admin as temporary admin UI** with these caveats: + +1. **MVP Scope**: 10 core models + auth + permissions + bulk actions (3-4 weeks) +2. **Deferred Features**: Maps, custom forms, specialized apps (re-add post-migration) +3. **Parallel Operation**: Keep OcotilloUI available as fallback for first month +4. **User Training**: Brief staff on UX differences and workarounds + +**Total Estimated Effort**: **32-42 hours** (1 developer, 4 weeks part-time) + +**Risk Level**: **Low-Medium** (core functionality is straightforward, deferred features are edge cases) + +**Value**: **High** (unblocks AMPAPI migration, validates new database schema, reduces dependency on React frontend) + +--- + +## Appendix: Model Mapping + +### NMSampleLocations Models → Starlette Admin Views + +From `db/__init__.py`, NMSampleLocations has **50+ models**. OcotilloUI currently exposes **~15 resources**. Here's the mapping: + +| NMSampleLocations Model | OcotilloUI Resource | Starlette Admin Priority | +|------------------------|---------------------|-------------------------| +| `Asset` | ✅ asset | P0 - Core MVP | +| `Contact` | ✅ contact | P0 - Core MVP | +| `Location` | ✅ location | P0 - Core MVP | +| `Sensor` | ✅ sensor | P0 - Core MVP | +| `Sample` | ✅ sample | P0 - Core MVP | +| `Group` | ✅ group | P0 - Core MVP | +| `ThingWell` | ✅ well | P0 - Core MVP | +| `ThingSpring` | ✅ spring | P0 - Core MVP | +| `ThingIdLink` | ✅ thing-id-link | P0 - Core MVP | +| `WellScreen` | ✅ well-screen | P0 - Core MVP | +| `GroundwaterLevelObservation` | ✅ groundwater-level-observation | P0 - Core MVP | +| `LexiconCategory` | ✅ lexicon (category) | P0 - Core MVP | +| `LexiconTerm` | ✅ lexicon (term) | P0 - Core MVP | +| `Deployment` | ❌ Not in OcotilloUI | P1 - Add later | +| `Observation` | ❌ Not in OcotilloUI (base class) | P1 - Add later | +| `Thing` | ❌ Not in OcotilloUI (base class) | P1 - Add later | +| `Publication` | ❌ Not in OcotilloUI | P2 - Add later | +| `DataProvenance` | ❌ Not in OcotilloUI | P2 - Add later | +| `AquiferSystem` | ❌ Not in OcotilloUI | P2 - Add later | +| `GeologicFormation` | ❌ Not in OcotilloUI | P2 - Add later | +| *(35+ more models)* | ❌ Not exposed in OcotilloUI | P2/P3 - Future | + +**Key Insight**: OcotilloUI only exposes **~25% of NMSampleLocations models**. Starlette Admin can initially match this subset, then expand to cover more models over time. + +--- + +## Next Steps + +1. **Review this document** with stakeholders (data managers, developers) +2. **Confirm MVP scope** - are deferred features acceptable? +3. **Prototype Starlette Admin** with 2-3 models (4-6 hours) +4. **User acceptance testing** - show prototype to staff, gather feedback +5. **Go/No-Go decision** based on UAT feedback +6. **Full implementation** if approved (follow 4-week roadmap) + +--- + +## Resources + +- [Starlette Admin Documentation](https://jowilf.github.io/starlette-admin/) +- [Starlette Admin SQLAlchemy Example](https://github.com/jowilf/starlette-admin/tree/main/examples/sqla) +- [ADMIN_FRAMEWORK_EVALUATION.md](./ADMIN_FRAMEWORK_EVALUATION.md) - Technical framework comparison +- [OcotilloUI Source Code](../../OcotilloUI/) - Current admin implementation +- [NMSampleLocations Models](../db/) - Database schema