Add Starlette Admin interface for Location management (MS Access replacement)#331
Conversation
Install starlette-admin[i18n] for web-based admin interface. This will replace MS Access for managing database records, providing web-based CRUD operations with authentication and RBAC. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Create core admin infrastructure: - auth.py: Authentik OIDC authentication provider • Integrates with existing authentication system • Maps Authentik groups to admin roles (Admin/Editor/Viewer) • Reuses JWT token verification from core.permissions - fields.py: Custom WKT field for PostGIS geometry • Converts WKBElement ↔ WKT string for form input • Validates WKT format (POINT(lon lat)) • Includes help text for staff transitioning from UTM coordinates - config.py: Admin initialization and configuration • Creates Admin instance with engine and auth provider • Mounts admin at /admin route • Ready to register model views - __init__.py: Package initialization 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Implement comprehensive admin view for Location model: List View (MS Access Datasheet equivalent): - Sortable columns: id, description, county, state, elevation - Filters: county, state, release_status, elevation, created_at - Search: description, county, state, quad_name - Export: CSV and Excel - Pagination: 50 records per page (configurable) Form View (MS Access Form equivalent): - WKT coordinate input with detailed help text - Grouped fields: Basic Info, Geographic Info, Notes - Elevation in meters (NAVD88 vertical datum) - Release status (draft/published) Permissions (RBAC): - Admin: Create, edit, delete all locations - Editor: Create and edit, cannot delete - Viewer: View published locations only (read-only) Bulk Actions: - Publish selected locations - Unpublish selected locations (set to draft) This replicates MS Access functionality staff are accustomed to, with improvements like multi-user access and better security. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Integrate admin interface into FastAPI application: - Import and call create_admin(app) in main.py - Admin accessible at http://localhost:8000/admin - Runs alongside existing API routes Staff can now access admin interface via web browser instead of opening MS Access .accdb file. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR implements a web-based Starlette Admin interface to replace MS Access for managing Location records during the AMPAPI → NMSampleLocations migration. The interface integrates with existing Authentik OIDC authentication and provides familiar MS Access-like functionality including list views, forms, filtering, and bulk operations.
Key Changes:
- Added Starlette Admin dependency for web-based database management
- Implemented OIDC authentication integration with Authentik
- Created Location admin view with RBAC permissions (Admin/Editor/Viewer roles)
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| pyproject.toml | Added starlette-admin[i18n] dependency for admin interface functionality |
| main.py | Mounted admin interface at /admin route |
| admin/init.py | Package initialization exporting create_admin function |
| admin/config.py | Admin configuration with Location view registration |
| admin/auth.py | Authentik OIDC authentication provider with role mapping |
| admin/fields.py | Custom WKT field handlers for PostGIS geometry columns |
| admin/views.py | Location model admin view with list/form/permission configuration |
| Only admins can create new locations. | ||
|
|
||
| Returns: | ||
| bool: True if user has 'admin' role | ||
| """ | ||
| user = getattr(request.state, "user", None) | ||
| if user is None: | ||
| return False | ||
| return "admin" in getattr(user, "roles", []) |
There was a problem hiding this comment.
The docstring states "Only admins can create new locations" but according to the PR description's permission table, both Admins and Editors should have create permissions. Either update the docstring to match the actual permission model or update the implementation to allow editors to create locations.
| Only admins can create new locations. | |
| Returns: | |
| bool: True if user has 'admin' role | |
| """ | |
| user = getattr(request.state, "user", None) | |
| if user is None: | |
| return False | |
| return "admin" in getattr(user, "roles", []) | |
| Admins and editors can create new locations. | |
| Returns: | |
| bool: True if user has 'admin' or 'editor' role | |
| """ | |
| user = getattr(request.state, "user", None) | |
| if user is None: | |
| return False | |
| roles = getattr(user, "roles", []) | |
| return "admin" in roles or "editor" in roles |
Create comprehensive Gherkin/Behave feature files documenting admin interface behavior and acceptance criteria. Features: - authentication.feature: Auth/authz flows, RBAC, JWT verification • 11 scenarios covering admin/editor/viewer roles • Security tests (invalid tokens, expired tokens) • Development mode behavior - location_admin.feature: Location CRUD operations • 24 scenarios covering full admin lifecycle • List view: search, filter, sort, pagination, export • Forms: create/edit with WKT coordinate validation • Bulk actions: publish/unpublish • Data visibility by release status • Permission checks for all operations - README.md: Documentation for running and extending tests • Test setup instructions • Tag reference • Example test runs • Troubleshooting guide These feature files serve as both: 1. Living documentation of admin behavior 2. Acceptance criteria for implementation 3. Automated test specifications (when step definitions added) Tags enable targeted test runs: behave features/admin/ --tags=@smoke behave features/admin/ --tags=@rbac behave features/admin/ --tags=@bulk-actions Next step: Implement step definitions using Playwright/FastAPI TestClient 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Fixes CI failures: - Replaced Unicode smart quotes with ASCII quotes - admin/fields.py: Fixed bullet points in WKT help text - admin/views.py: Fixed all string literals with smart quotes This resolves: - SyntaxError in fields.py line 120 - UnicodeDecodeError in views.py (byte 0x92)
| Examples: | ||
| | role | email | can_create | can_edit | can_delete | visibility | | ||
| | Admin | admin@nmbgmr.nmt.edu | should | should | should | should | | ||
| | Editor | editor@nmbgmr.nmt.edu | should | should | should not | should | |
There was a problem hiding this comment.
The test expectation shows Editor can delete ('should' in can_delete column), but according to the permission table in the PR description and the can_delete implementation in admin/views.py, Editors should not be able to delete. This test data appears incorrect.
|
There is a lot to discuss regarding this PR. First this concept is/was being explored via fastapi-admin. Second ocotillo has this "admin" functionality. Third, user research and experience have repeatedly revealed and reinforced that users do not want single table interfaces- this was a major complaint when users used NMAQuiferDev. Lastly, given that end users will not use it, and developers already have clients available for database management, who is the audience for this functionality? Developing a consensus around this will be important moving forward |
|
@jirhiker Yes, this is part of a proposal we will bring to you very shortly. More to come. |
|
There is considerable potential for this approach, and this appears to be a well-designed and clean implementation. However, the issue we have always run into is that ultimately we find ourselves having to write a ton of custom JS to implement the functionality the users (or even admins) really want (Perhaps this is more a function of NMAquifers' poor data model than an issue with the monolithic approach). The need for a bunch of custom JS/CSS/HTML has led us to move all Web GUI development to JS/React applications. The need for responsive, user-friendly, modern UIs eventually supersedes the benefits of a monolithic server-side application. So if there are clear guardrails on what the Admin UI is for and who should know about it/use it then I think this could be made a priority |
|
does MS Access support such functionality? |
|
I'm not sure which functionality you are referring to. However, I don't think that using ms access/ampapi as a baseline is valuable in this case. Limiting ourselves to what ms access did even temporarily does not seem a worthwhile goal. The previous workflow for data entry via ms access and nmaquiiferdev were highly compromised and bespoke because of both technical and culture issues. The Ms access-centric view also neglects the fact that a significant number of users interact with our data entirely independently of Ms access. Only a small number of users enter data (2-3) via Ms access, a slightly larger group may have used Ms access to view data, however the majority of users worked with our data either via the "interactive web map" or with arcgis via data exports (only a few users were capable and confident enough to us Ms access to generate data exports) |
The `description` field was missing parentheses on `mapped_column`, causing a syntax error when the model was loaded. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extends starlette_admin's AdminUser with a roles list to enable role-based access control in admin views. The roles are populated from Authentik groups during authentication. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Refactor admin views into a package structure (admin/views/) and add MS Access-style admin interfaces for core data models: - ThingAdmin: Wells, springs, construction details - ObservationAdmin: Water level measurements - ContactAdmin: Well owners and managers - SensorAdmin: Equipment inventory - DeploymentAdmin: Equipment installation log Each view includes: - List view with sorting, filtering, search, pagination - Create/Edit forms with field labels and help text - RBAC: Admin (full), Editor (create/edit), Viewer (published only) - Bulk actions: Publish/Unpublish selected records - Export: CSV and Excel formats This replaces the single views.py file with a modular package structure for better maintainability as more views are added. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add 8 new equipment type mappings to sensor_transfer.py: Precip Collector, Soil Moisture, Weather Station, Camera, Weir, Snow Lysimeter, Tipping Bucket, Lysimeter - Add 4 missing sensor types to lexicon.json: Weather Station, Weir, Snow Lysimeter, Lysimeter This fixes 71 equipment records that were previously skipping due to unmapped EquipmentType values. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use PointID as the location description field to satisfy the NOT NULL constraint on location.description. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When TRANSFER_LIMIT was set to -1 (meaning "no limit"), the condition `if limit and i >= limit` evaluated to True on first iteration because -1 is truthy and 0 >= -1. This caused transfers to exit immediately without processing any records. Changed condition to `if limit > 0 and i >= limit` (or with explicit None check) so that -1 and 0 correctly mean "no limit". 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Different wells (things) can have observation blocks with the same review_status, parameter_id, and overlapping time ranges. The previous unique constraint on (review_status, parameter_id, start_datetime, end_datetime) caused duplicate key errors. Changes: - Add thing_id foreign key to TransducerObservationBlock - Add Thing relationship for navigation - Update unique constraint to include thing_id - Update transfer code to extract thing_id from deployment before creating blocks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Legacy data from MS Access may not have descriptions for all locations. Making this field nullable allows the transfer to proceed without requiring a description value. Changes: - Update Location model to set nullable=True on description - Add migration to alter the column constraint 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Configure SQLAlchemy connection pooling with configurable pool_size and max_overflow settings via environment variables. This enables concurrent database connections required for parallel transfer operations. - Add DB_POOL_SIZE and DB_MAX_OVERFLOW env vars (defaults: 10, 20) - Enable pool_pre_ping to verify connections before use - Apply to both CloudSQL and local PostgreSQL engines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Restructure transfer pipeline to execute independent transfers concurrently using ThreadPoolExecutor. Transfers are organized into phases: Phase 1: Foundational (AquiferSystems, GeologicFormations) - parallel Phase 2: Wells - supports parallel mode via TRANSFER_PARALLEL_WELLS Phase 3: Group 1 (Screens, Contacts, WaterLevels, etc.) - parallel Phase 4: Sensors - sequential (required before continuous water levels) Phase 5: Group 2 (Pressure, Acoustic) - parallel Add helper functions for thread-safe transfer execution with timing. Retain sequential mode via TRANSFER_PARALLEL=0 for compatibility. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement row-level parallelization for wells transfer using batch processing with ThreadPoolExecutor. Key optimizations: - transfer_parallel(): Splits wells into batches across workers - _step_parallel_complete(): Creates well + ALL dependent objects (notes, status, provenances, measuring points, formation zone) in a single pass, eliminating the sequential after_hook bottleneck - Thread-safe aquifer handling with minimal lock contention - Pre-load formations per batch to avoid race conditions Performance: 9,887 wells in ~2 minutes (vs ~56 minutes with after_hook) Throughput: ~21 wells/sec with 4 workers Enable via TRANSFER_PARALLEL_WELLS=1, configure workers with TRANSFER_WORKERS (default: 4). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…hod for compatibility
|
Right now there is a lot of duplicated code in the ModelView subclasses. It would be great to consolidate functionality into a OcotilloModelView subclass. The duplicated code im referring to is stuff like this |
…ion-ms-access-jir Feature/admin location ms access jir
…leaner configuration
…eamline upgrade process
…, Data Provenance, Notes, Sample, and Group models
Summary
This PR implements a web-based admin interface to replace MS Access for managing Location records during the AMPAPI → NMSampleLocations migration.
Key Features:
Context: MS Access → Starlette Admin Migration
Staff are accustomed to using MS Access to manage the AMPAPI SQL Server database. This PR provides equivalent functionality in a web-based interface.
What Staff Are Gaining
✅ Multi-user access - No more "database is locked" errors
✅ Remote access - Work from anywhere (no VPN/RDP required)
✅ No file corruption - PostgreSQL is more robust than .accdb files
✅ Better security - User-level permissions vs. file permissions
✅ Audit trail - Track who changed what when
✅ Cross-platform - Works on Mac/Linux/iPad, not just Windows
What Staff Are Losing
❌ Offline access - Requires internet connection
⚠️ Complex queries - Use filters instead of Access query designer
⚠️ Custom reports - Export to CSV → Excel instead
AMPAPI → NMSampleLocations Schema Changes
Primary Key: GUID → Auto-Increment Integer
AMPAPI (SQL Server):
NMSampleLocations (PostgreSQL):
Coordinates: UTM → WGS84 Point Geometry
AMPAPI stored UTM Zone 12N/13N coordinates in separate fields:
NMSampleLocations stores WGS84 coordinates as PostGIS POINT:
For Staff: Instead of entering Easting/Northing/UTM Zone, enter:
Note: Longitude first, then latitude (not lat/lon!)
Release Status: Boolean → Lexicon Term
AMPAPI:
NMSampleLocations:
Migration Mapping:
PublicRelease=True→release_status='published'PublicRelease=False→release_status='draft'Elevation: Feet → Meters
AMPAPI:
NMSampleLocations:
Conversion: Feet → meters during migration
Site Type & PointID: Moved to Thing Table
AMPAPI stored PointID and SiteType in Location table.
NMSampleLocations moved these to Thing table:
Admin Interface Details
List View (MS Access Datasheet Equivalent)
Columns Displayed:
Filters:
Search:
Actions:
Form View (MS Access Form Equivalent)
Fields:
POINT(lon lat)Read-Only Fields (migration data):
Permissions
Implementation Details
Files Added
admin/__init__.py- Package initializationadmin/auth.py- Authentik OIDC authentication provideradmin/config.py- Admin configurationadmin/fields.py- Custom WKT field for PostGIS geometryadmin/views.py- LocationAdmin viewFiles Modified
main.py- Mount admin at/adminpyproject.toml- Add starlette-admin dependencyuv.lock- Update lock fileTesting Checklist
/admin(redirects to login if not authenticated)POINT(-106.123 35.456)Next Steps
After this PR:
Related Documentation
/AMPAPI/models/location.py/NMSampleLocations/db/location.py