open-navigator / web_docs /docs /development /state-field-naming-standard.md
jcbowyer's picture
Clean HuggingFace deployment without binary files
e59d91d
|
Raw
History Blame Contribute Delete
8.62 kB
---
sidebar_position: 5
---
# State Field Naming Standard
## Overview
This document defines the **mandatory naming convention** for state-related fields across all Open Navigator databases, parquet files, and code.
## Standard
### Required Field Names
| Field Name | Data Type | Purpose | Example Values |
|------------|-----------|---------|----------------|
| `state_code` | VARCHAR(2) or string | Two-letter state abbreviation | `'AL'`, `'MA'`, `'WI'` |
| `state` | VARCHAR(50) or string | Full state name | `'Alabama'`, `'Massachusetts'`, `'Wisconsin'` |
### Rules
βœ… **DO THIS:**
- Use `state_code` for all 2-letter state abbreviations
- Use `state` for all full state names
- Include BOTH fields when storing state information
- Use uppercase for `state_code` values
❌ **DON'T DO THIS:**
- ❌ Use `state` for 2-letter codes (legacy pattern - being phased out)
- ❌ Use `state_abbr`, `state_abbreviation`, or `st_abbr`
- ❌ Use `state_name` (use `state` instead)
- ❌ Store state codes in lowercase
## Code Examples
### Python/Pandas
```python
# βœ… CORRECT
df = pd.DataFrame({
'jurisdiction_name': ['Mobile', 'Boston'],
'state_code': ['AL', 'MA'],
'state': ['Alabama', 'Massachusetts']
})
# Save to parquet
df.to_parquet('jurisdictions.parquet')
# ❌ WRONG
df_wrong = pd.DataFrame({
'jurisdiction_name': ['Mobile'],
'state': ['AL'], # Don't use 'state' for 2-letter codes
'state_name': ['Alabama'] # Don't use 'state_name'
})
```
### SQL Schema
```sql
-- βœ… CORRECT
CREATE TABLE jurisdiction (
id SERIAL PRIMARY KEY,
jurisdiction_name VARCHAR(200) NOT NULL,
state_code VARCHAR(2) NOT NULL,
state VARCHAR(50) NOT NULL,
CONSTRAINT check_state_code_length CHECK (LENGTH(state_code) = 2),
CONSTRAINT check_state_code_uppercase CHECK (state_code = UPPER(state_code))
);
-- ❌ WRONG
CREATE TABLE jurisdictions_legacy (
id SERIAL PRIMARY KEY,
name VARCHAR(200),
state VARCHAR(2), -- Ambiguous: is this a code or full name?
state_abbr VARCHAR(2) -- Don't use state_abbr
);
```
### API Queries
```python
# βœ… CORRECT - FastAPI
@app.get("/api/jurisdictions")
async def get_jurisdictions(
state_code: Optional[str] = Query(None, regex="^[A-Z]{2}$", description="Two-letter state code"),
state: Optional[str] = Query(None, description="Full state name")
):
query = "SELECT * FROM jurisdiction WHERE 1=1"
params = []
if state_code:
query += f" AND state_code = ${len(params)+1}"
params.append(state_code)
if state:
query += f" AND state = ${len(params)+1}"
params.append(state)
# ... execute query
# ❌ WRONG
async def get_jurisdictions_wrong(state: str): # Ambiguous parameter name
query = f"SELECT * FROM jurisdictions WHERE state = '{state}'" # SQL injection risk + ambiguous
```
## Migration Guide
### Current State (as of 2026-05)
Many existing tables and parquet files use the **legacy convention**:
- `state` = 2-letter code (e.g., 'AL', 'MA')
- `state_name` = full name (if exists) OR missing entirely
### Migration Steps
**For Database Tables:**
1. Add new `state_code` column:
```sql
ALTER TABLE jurisdiction ADD COLUMN state_code VARCHAR(2);
UPDATE jurisdiction SET state_code = state;
ALTER TABLE jurisdiction ALTER COLUMN state_code SET NOT NULL;
```
2. Add/rename state name column:
```sql
-- If state_name exists:
ALTER TABLE jurisdiction RENAME COLUMN state_name TO state_temp;
ALTER TABLE jurisdiction ADD COLUMN state VARCHAR(50);
UPDATE jurisdiction SET state = state_temp;
ALTER TABLE jurisdiction DROP COLUMN state_temp;
-- If state_name doesn't exist:
ALTER TABLE jurisdiction ADD COLUMN state VARCHAR(50);
UPDATE jurisdiction SET state = (
CASE state_code
WHEN 'AL' THEN 'Alabama'
WHEN 'AK' THEN 'Alaska'
-- ... etc
END
);
```
3. Drop old `state` column and rename:
```sql
ALTER TABLE jurisdiction DROP COLUMN state_old;
```
**For Parquet Files:**
```python
import pandas as pd
from pathlib import Path
def migrate_parquet(file_path: Path):
"""Migrate parquet file to new naming convention."""
df = pd.read_parquet(file_path)
# If using legacy convention
if 'state' in df.columns and df['state'].str.len().max() == 2:
# Rename state β†’ state_code
df.rename(columns={'state': 'state_code'}, inplace=True)
# Add full state name
state_map = {
'AL': 'Alabama', 'AK': 'Alaska', 'AZ': 'Arizona',
# ... full mapping
}
df['state'] = df['state_code'].map(state_map)
# If state_name exists, rename to state
if 'state_name' in df.columns:
df.rename(columns={'state_name': 'state'}, inplace=True)
# Save back
df.to_parquet(file_path)
```
### Tables Requiring Migration
Based on current schema audit (2026-05-03):
| Table Name | Current | Needs Migration |
|------------|---------|----------------|
| `jurisdiction` | `state` (2-char) | βœ… Yes |
| `contact` | `state` (2-char) | βœ… Yes |
| `event` | `state` (2-char) | βœ… Yes |
| `organization_nonprofit` | `state` (2-char) | βœ… Yes |
| `bills_search` | `state` (2-char) | βœ… Yes |
| `bill_map_aggregate` | `state_code` (2-char) | βœ… Needs `state` added |
| `zip_county_mapping` | `state_abbr` (2-char) | βœ… Rename to `state_code` |
| `jurisdictions_details_search` | `state` (2-char) | βœ… Yes |
### Parquet Files Requiring Migration
```
data/gold/states/{STATE}/
β”œβ”€β”€ contact_official.parquet (βœ… needs migration)
β”œβ”€β”€ contacts_local_officials.parquet (βœ… needs migration)
β”œβ”€β”€ events.parquet (βœ… needs migration)
β”œβ”€β”€ jurisdictions_details.parquet (βœ… needs migration)
└── nonprofits_organizations.parquet (βœ… needs migration)
```
## State Code β†’ Full Name Mapping
### Complete U.S. State Mapping
```python
STATE_CODE_TO_NAME = {
'AL': 'Alabama', 'AK': 'Alaska', 'AZ': 'Arizona', 'AR': 'Arkansas',
'CA': 'California', 'CO': 'Colorado', 'CT': 'Connecticut', 'DE': 'Delaware',
'FL': 'Florida', 'GA': 'Georgia', 'HI': 'Hawaii', 'ID': 'Idaho',
'IL': 'Illinois', 'IN': 'Indiana', 'IA': 'Iowa', 'KS': 'Kansas',
'KY': 'Kentucky', 'LA': 'Louisiana', 'ME': 'Maine', 'MD': 'Maryland',
'MA': 'Massachusetts', 'MI': 'Michigan', 'MN': 'Minnesota', 'MS': 'Mississippi',
'MO': 'Missouri', 'MT': 'Montana', 'NE': 'Nebraska', 'NV': 'Nevada',
'NH': 'New Hampshire', 'NJ': 'New Jersey', 'NM': 'New Mexico', 'NY': 'New York',
'NC': 'North Carolina', 'ND': 'North Dakota', 'OH': 'Ohio', 'OK': 'Oklahoma',
'OR': 'Oregon', 'PA': 'Pennsylvania', 'RI': 'Rhode Island', 'SC': 'South Carolina',
'SD': 'South Dakota', 'TN': 'Tennessee', 'TX': 'Texas', 'UT': 'Utah',
'VT': 'Vermont', 'VA': 'Virginia', 'WA': 'Washington', 'WV': 'West Virginia',
'WI': 'Wisconsin', 'WY': 'Wyoming', 'DC': 'District of Columbia',
'PR': 'Puerto Rico', 'VI': 'U.S. Virgin Islands', 'GU': 'Guam',
'AS': 'American Samoa', 'MP': 'Northern Mariana Islands'
}
STATE_NAME_TO_CODE = {v: k for k, v in STATE_CODE_TO_NAME.items()}
```
## Rationale
### Why This Standard?
1. **Clarity**: `state_code` and `state` are unambiguous
2. **Consistency**: Aligns with industry standards (LocalView dataset uses `state_name`)
3. **Prevents Errors**: No confusion about whether `state` contains 'AL' or 'Alabama'
4. **Better UX**: API consumers get both formats without needing conversion
5. **Query Optimization**: Can filter by either format efficiently
### Comparison with Other Standards
| Standard | 2-Letter Code | Full Name | Notes |
|----------|---------------|-----------|-------|
| **Open Navigator** | `state_code` | `state` | βœ… Recommended |
| LocalView (Harvard) | (none) | `state_name` | Good, but incomplete |
| Legacy databases | `state` or `state_abbr` | `state_name` | ❌ Ambiguous |
| Census Bureau | `STUSAB` | `NAME` | Federal standard |
## Enforcement
### Pre-commit Checks
Add to `.github/workflows/ci-build-test.yml`:
```yaml
- name: Check State Field Naming
run: |
python scripts/validation/check_state_naming.py
```
### Linting Rules
For new code:
- Reject PRs with `state VARCHAR(2)` in SQL
- Reject PRs with `state_name` or `state_abbr` fields
- Require both `state_code` and `state` when state info is included
## See Also
- [Database Schema Documentation](../data-sources/database-schema.md)
- [Migration Scripts](../../scripts/migrations/)
- [Data Pipeline Standards](./data-pipeline-standards.md)