Skip to content

feat(geo): baseapp.geo package — attach geometries to any object (map query with bbox/radius, plugin arch) - #445

Draft
nossila wants to merge 36 commits into
masterfrom
feature/baseapp_geo
Draft

feat(geo): baseapp.geo package — attach geometries to any object (map query with bbox/radius, plugin arch)#445
nossila wants to merge 36 commits into
masterfrom
feature/baseapp_geo

Conversation

@nossila

@nossila nossila commented Jul 19, 2026

Copy link
Copy Markdown
Member

Summary

Adds the baseapp.geo package — a plugin-architecture geo feature that attaches geometries to any DocumentId-enabled object, following the same namespace layout as baseapp.files (#442).

  • Model: swappable AbstractGeoJSONFeature (DocumentIdTargetMixin, TimeStampedModel, DocumentIdMixin, RelayModel) with name/description (optional), feature_type category, and geometry = GeometryField(geography=True, srid=4326) restricted to Point|Polygon (validated in clean() and the mutation form). Concrete models live in consumers; swap via BASEAPP_GEO_GEOJSONFEATURE_MODEL.
  • GraphQL: RFC 7946 Feature-shaped GeoJSONFeature type (vendored — upstream graphql_geojson.GeoJSONType was spike-tested and rejected: it swallows interface fields into properties under graphene 3, issue Using of this module makes types to be incompatible with custom interfaces flavors/django-graphql-geojson#4). The graphql_geojson Geometry scalar ships with a graphene-3 parse_literal shim.
  • Map query: geoFeatures filtered connection (max_limit=100, totalCount) with:
    • bbox: "minLon,minLat,maxLon,maxLat"__intersects, antimeridian (west > east) split into two OR-ed boxes
    • near: "lng,lat,radiusMeters"__dwithin (meters-correct via geography column), radius capped at 100 km
    • featureType, targetObjectId (uuid public-id and legacy base64 relay ids)
    • malformed filter input raises ValidationError — never a silently unfiltered result
  • Mutations: geoFeatureCreate (Edge payload) / geoFeatureUpdate / geoFeatureDelete (deletedId + target), permission check before any write, form-validated.
  • Permissions: GeoPermissionsBackend — public read (incl. anonymous), authenticated create, change/delete via standard Django model perms. Enforced at get_node, mutations, and PermissionsInterface.hasPerm.
  • Plugin architecture: GeoPlugin entry point under baseapp.plugins; schema composes via the plugin registry — zero schema-file edits in consumers.
  • Admin (unfold via admin_helpers), README, testproject wiring + migrations (pgtriggers included).

Package layout

Namespace layout per #442: module path is baseapp.geo (baseapp/geo/), while the Django app label stays baseapp_geo — so swapper settings (BASEAPP_GEO_*), the AUTHENTICATION_BACKENDS slot key, and consumer migrations are label-based and unaffected by the module path.

INSTALLED_APPS += ["django.contrib.gis", "baseapp.geo", "<your concrete app>"]
BASEAPP_GEO_GEOJSONFEATURE_MODEL = "geo.GeoJSONFeature"

Test plan

  • 90 tests, 99.43% coverage (floor 75%), zero mocks — real PostGIS + composed-schema GraphQL clients
  • Includes: metric-correctness bracket (~1113 m point in at r=1200, out at r=1000; polygon nearest-edge), antimeridian both-sides, malformed-input error paths, full permission matrix, CRUD + no-persist paths, public-id node lookup
  • flake8 / black / isort / makemigrations --check clean
  • E2E verified over live HTTP: seeded feature returned as GeoJSON Feature through bbox+near on a running server

🤖 Generated with Claude Code

https://claude.ai/code/session_01DDeemQ7iF4D3VhAwQVui9n

nossila and others added 30 commits July 18, 2026 19:51
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDeemQ7iF4D3VhAwQVui9n
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDeemQ7iF4D3VhAwQVui9n
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDeemQ7iF4D3VhAwQVui9n
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDeemQ7iF4D3VhAwQVui9n
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDeemQ7iF4D3VhAwQVui9n
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDeemQ7iF4D3VhAwQVui9n
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDeemQ7iF4D3VhAwQVui9n
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDeemQ7iF4D3VhAwQVui9n
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 65fa3c44-0832-4576-afe9-ca0049f0321b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/baseapp_geo

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Mirror baseapp.files (PR #442): module path baseapp.geo, app label stays
baseapp_geo so swapper settings, backend slot keys and consumer
migrations are unchanged. Entry point now baseapp_geo = "baseapp.geo.plugin:GeoPlugin";
AppConfig aligned with namespace convention (BigAutoField).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDeemQ7iF4D3VhAwQVui9n
@nossila nossila changed the title feat(geo): add baseapp_geo plugin package feat(geo): baseapp.geo package — attach geometries to any object (map query with bbox/radius, plugin arch) Jul 25, 2026
CI runs isort via uv run with the project installed, which classifies
baseapp.* as first-party; dev containers skip project install and
missed the reorder.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDeemQ7iF4D3VhAwQVui9n
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant