Files
bls-data/bls_client/queries/employment.py
Dave Boyd 4fe734381e Fix broken series-ID builders; env-var config; project README
Repair every dead series-ID encoding (library now 82/82 query helpers
return live data, verified against the BLS API):

- JOLTS: 21-char format (was 18) — add state/area/sizeclass fields
- OES: national area code 0000000 (was invalid 0000400)
- ECI: correct owner/component/estimate encoding, default unadjusted (CIU)
- Productivity: 4-digit sector + 4-digit measure codes (was 2+3)
- QCEW: 13-char timeseries-API form (ENUUS00010510 / ENU{fips}00010{own}10)
- PPI: repoint finished-goods -> final demand (WPUFD4); keep alias
- ECEC: drop fabricated health-insurance/retirement helpers; add total benefits
- wages SOC: software developers 151132 -> 151252 (2018 SOC)

Tooling/docs:
- config reads BLS_API_KEY env var (takes precedence; config.py gitignored)
- add requirements.txt
- rewrite README as project front door + coverage table + limitations
- correct JOLTS/OES tables in series_id_formats.md, USAGE.md, dataset explorer

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 09:45:27 -04:00

140 lines
4.8 KiB
Python

"""
Pre-built employment series IDs — ready to pass to BLSClient.fetch().
All functions return a single series ID string or a dict of {label: series_id}.
"""
from ..series import laus_state, laus_msa, ces_national, ces_state, jolts
# ---------------------------------------------------------------------------
# National employment
# ---------------------------------------------------------------------------
def nonfarm_payrolls(seasonal: bool = True) -> str:
"""Total nonfarm payroll employment (CES). The headline monthly jobs number."""
return ces_national("total_nonfarm", "employees", seasonal)
def private_payrolls(seasonal: bool = True) -> str:
"""Total private sector payroll employment."""
return ces_national("total_private", "employees", seasonal)
def government_employment(seasonal: bool = True) -> str:
"""Total government employment (federal + state + local)."""
return ces_national("government", "employees", seasonal)
def manufacturing_employment(seasonal: bool = True) -> str:
return ces_national("manufacturing", "employees", seasonal)
def national_unemployment_rate(seasonal: bool = True) -> str:
"""National unemployment rate from the CPS (U-3 rate)."""
return "LNS14000000" if seasonal else "LNU04000000"
def national_labor_force_participation(seasonal: bool = True) -> str:
"""National labor force participation rate."""
return "LNS11300000" if seasonal else "LNU01300000"
# ---------------------------------------------------------------------------
# LAUS — state unemployment
# ---------------------------------------------------------------------------
def dc_unemployment_rate(seasonal: bool = False) -> str:
return laus_state(11, "rate", seasonal)
def md_unemployment_rate(seasonal: bool = False) -> str:
return laus_state(24, "rate", seasonal)
def va_unemployment_rate(seasonal: bool = False) -> str:
return laus_state(51, "rate", seasonal)
def state_unemployment(state_fips: int, seasonal: bool = False) -> dict:
"""All four LAUS measures for a state."""
return {
"rate": laus_state(state_fips, "rate", seasonal),
"unemployed": laus_state(state_fips, "unemployed", seasonal),
"employed": laus_state(state_fips, "employed", seasonal),
"laborforce": laus_state(state_fips, "laborforce", seasonal),
}
def dc_region_unemployment() -> dict:
"""Unemployment rates for DC, MD, VA states + DC Metro, Baltimore, Richmond MSAs."""
return {
"DC State": laus_state(11, "rate"),
"Maryland": laus_state(24, "rate"),
"Virginia": laus_state(51, "rate"),
"DC Metro MSA": laus_msa(11, 47900, "rate"),
"Baltimore MSA":laus_msa(24, 12580, "rate"),
"Richmond MSA": laus_msa(51, 40060, "rate"),
}
# ---------------------------------------------------------------------------
# JOLTS
# ---------------------------------------------------------------------------
def job_openings_level(seasonal: bool = True) -> str:
return jolts("job_openings", "L", seasonal=seasonal)
def job_openings_rate(seasonal: bool = True) -> str:
return jolts("job_openings", "R", seasonal=seasonal)
def quits_level(seasonal: bool = True) -> str:
return jolts("quits", "L", seasonal=seasonal)
def hires_level(seasonal: bool = True) -> str:
return jolts("hires", "L", seasonal=seasonal)
def layoffs_level(seasonal: bool = True) -> str:
return jolts("layoffs", "L", seasonal=seasonal)
def jolts_dashboard() -> dict:
"""All five JOLTS measures (levels, SA) as a labeled dict."""
return {
"Job Openings": job_openings_level(),
"Hires": hires_level(),
"Quits": quits_level(),
"Layoffs": layoffs_level(),
"Total Separations": jolts("total_separations", "L"),
}
# ---------------------------------------------------------------------------
# QCEW
# ---------------------------------------------------------------------------
def qcew_national_private() -> str:
"""QCEW — national, private sector, all industries (monthly employment)."""
return "ENUUS00010510"
def qcew_dc_private() -> str:
"""QCEW — DC, private sector, all industries."""
return qcew_state(11, "5")
def qcew_state(state_fips: int, ownership: str = "5") -> str:
"""
QCEW state-level series.
Format: EN + U + area(5: 2-digit FIPS + "000") + datatype "1" + size "0"
+ ownership(1) + industry "10" (total, all industries) = 13 chars.
Args:
state_fips: 2-digit FIPS (e.g. 11=DC)
ownership: "0"=all, "5"=private, "1"=federal, "2"=state, "3"=local
Note: BLS serves the full QCEW catalog (county/industry detail) through its
dedicated QCEW Open Data API, not the timeseries API used here.
"""
return f"ENU{state_fips:02d}00010{ownership}10"