Metadata-Version: 2.4
Name: baltimore-patapsco
Version: 0.4.3
Summary: Shared Django runtime foundations for Baltimore civic applications
Author: Mayor's Office of Performance and Innovation
License-Expression: MIT
License-File: LICENSE
Requires-Dist: asgiref>=3.9,<4.0
Requires-Dist: django>=6.0,<7.0
Requires-Dist: djangorestframework>=3.16,<4.0
Requires-Dist: drf-spectacular>=0.28,<0.30
Requires-Dist: sentry-sdk[django]>=2.64.0,<3.0 ; extra == 'observability'
Requires-Dist: dj-database-url>=3.0,<4.0 ; extra == 'runtime'
Requires-Dist: django-environ>=0.12,<0.14 ; extra == 'runtime'
Requires-Dist: whitenoise>=6.9,<7.0 ; extra == 'runtime'
Requires-Python: >=3.13
Provides-Extra: observability
Provides-Extra: runtime
Description-Content-Type: text/markdown

# baltimore-patapsco (Python)

Reusable Django runtime primitives for Baltimore civic applications.

```python
INSTALLED_APPS += ["baltimore.patapsco"]

REST_FRAMEWORK = {
    "EXCEPTION_HANDLER": "baltimore.patapsco.api.exception_handler",
}

urlpatterns = [path("", include("baltimore.patapsco.urls"))]
```

Place `RequestContextMiddleware` early in `MIDDLEWARE` — after
`SecurityMiddleware` (and WhiteNoise when present), before session, auth, and
anything that can short-circuit a response — so every later middleware, view,
and log line sees the bound request ID. The reference app shows the canonical
order:

```python
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    "baltimore.patapsco.observability.RequestContextMiddleware",
    # sessions, common, csrf, auth, messages, clickjacking...
]
```

## Stable health and readiness views

Applications that mount or extend the operational views directly use the public
facade:

```python
from baltimore.patapsco.api import HealthView, ReadinessView
```

`HealthView` is process-only liveness. `ReadinessView` runs the established
database check and returns its public-safe 200 or 503 status document. Both use
no authentication classes and `AllowAny`, and both remain ordinary DRF
`APIView` classes with `as_view()`.

An application may subclass either class, override `get()`, and call
`super().get(request)` before composing app-owned public-safe checks. The parent
response status, request ID, and database result remain the starting contract;
the application owns an explicit serializer and OpenAPI annotation for any
extended payload.

The previous `baltimore.patapsco.api.views` imports resolve to the same class
objects for compatibility. `DiagnosticsView` remains available only from that
module and through the packaged URL configuration; it is intentionally absent
from `baltimore.patapsco.api.__all__` because its staff/public policy is a
separate operational surface. The settings-free package root exports neither
view class.

`api_error_response()` and the DRF exception handler force every 5xx response to
the public `internal_error` code, generic message, and no details. Application
callers cannot opt a server failure out of that safety boundary.

Use `build_logging_config()` for the shared JSON/pretty log shape. Modules are
incrementally adoptable; installing the package does not enable auth, email, or
Sentry by itself.

## Sentry privacy defaults

Install the `observability` extra and call the shared initializer only when a DSN
is configured:

```python
from baltimore.patapsco.observability import configure_sentry

configure_sentry(
    environment="production",
    release="my-app@1.2.3",
    traces_sample_rate=0.1,
)
```

`configure_sentry()` is deny-by-default: it disables automatic PII, request-body
capture, and stack-frame local variables. Its event pipeline also removes request
cookies and query strings, strips query/fragment data from request URLs, and
recursively redacts Sentry's credential/session denylist plus common civic contact
and precise-location fields. Safe operational fields such as request IDs, routes,
and HTTP methods remain available.

Redaction is key-based. Applications must not attach sensitive values under
misleading keys or place resident data in exception messages. Product-specific
fields still require a privacy review before being added to Sentry context.

## Configuration surface

Everything the library reads from the environment or Django settings:

| Name                          | Kind           | Default               | Meaning                                                                                                                                                       |
| ----------------------------- | -------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `APP_ENV`                     | env var        | `local`               | Environment label in diagnostics and Sentry events; overridden by `PATAPSCO_ENVIRONMENT` when set                                                             |
| `APP_RELEASE`                 | env var        | —                     | Release identifier in diagnostics and Sentry events; overridden by `PATAPSCO_RELEASE` when set                                                                |
| `APP_LOG_FORMAT`              | env var        | `json`                | `json` or `pretty` output from `build_logging_config()`                                                                                                       |
| `APP_LOG_LEVEL`               | env var        | `INFO`                | Root log level from `build_logging_config()`                                                                                                                  |
| `SENTRY_DSN`                  | env var        | —                     | Enables `configure_sentry()`; absent means Sentry stays off                                                                                                   |
| `PATAPSCO_SERVICE_NAME`       | Django setting | project name          | Service label in the diagnostics payload                                                                                                                      |
| `PATAPSCO_ENVIRONMENT`        | Django setting | `APP_ENV` env var     | Overrides the diagnostics `environment` field via Django settings instead of the process environment (a real `override_settings` test seam)                   |
| `PATAPSCO_RELEASE`            | Django setting | `APP_RELEASE` env var | Overrides the diagnostics `release` field via Django settings instead of the process environment (a real `override_settings` test seam)                       |
| `PATAPSCO_DIAGNOSTICS_PUBLIC` | Django setting | `False`               | **Security-relevant:** flips `/api/diagnostics` from `IsAdminUser` to `AllowAny`. Leave `False` unless the deployment deliberately publishes runtime metadata |
