apiVersion: v3.1.0
kind: DataContract
id: education.k12_expenditures
name: k12_expenditures
version: 1.0.0
status: active
domain: education
tenant: georgia-public-data
description:
  purpose: 'K-12 expenditures for every Georgia public school, school district, and the state as a whole,
    by spending function, published by the Governor''s Office of Student Achievement (GOSA). Each row
    reports the total dollar amount and the per-FTE (full-time-equivalent student) dollar amount for one
    of 11 expenditure functions — instruction, pupil services, instructional support, school administration,
    general administration, maintenance and operations, transportation, media, school food services, debt
    services, and renovation and capital projects — covering school years 2010-11 through 2023-24. This
    is the full function-level spending ledger: all expenditures, broken out by function. The revenue
    side of the same GOSA report lives in the sibling topic `k12_revenues` (K-12 revenues by funding source,
    district/state detail); the two topics split the retired combined `revenues_and_expenditures` topic
    (one measure family per fact table) and share one bronze source, so they join cleanly on year and
    district_code. Distinct from the `per_pupil_expenditure` topic (FESR methodology), which is an included-cost
    per-pupil spending series whose totals intentionally differ from this full ledger.'
  usage: Star-schema fact table. Join the districts dimension on district_code; the schools dimension
    on district_code + school_code. Read directly with DuckDB over Parquet.
  limitations: 'State rows have NULL district_code and school_code; district rows have NULL school_code.
    The school detail level includes non-traditional entities (central offices, GNETS programs, Pre-K
    centers, maintenance facilities) in every year, and district detail includes the 16 RESA service-agency
    rollups (district codes 850-888) — exclude both when analyzing traditional schools/districts only.
    expenditure_per_pupil is not a true per-pupil amount everywhere: the 2024 file publishes it equal
    to expenditure_amount on every school-detail row (traditional schools included — 2024 school-level
    per-FTE values are totals, not per-pupil amounts), the 2023 file does so on all DOE OTHER school rows
    plus 151 other nonzero school rows, the 2012 file on its missing-FTE rows, and it frequently equals
    expenditure_amount on aggregate / non-school rows in other years — for a reliable school-level per-pupil
    spending series use the per_pupil_expenditure topic (FESR methodology) instead. State rows are published
    independently of district rows and do not always equal the sum of district values within a function
    (e.g. the 2014 renovation_and_capital_projects district sum runs about 5x the published state total)
    — treat each detail level as its own published series. This topic is the full function-level ledger;
    its totals intentionally differ from the FESR included-cost figures in per_pupil_expenditure. No suppression
    exists in this topic — NULL never means suppressed; the only NULL metric values are 475 expenditure_per_pupil
    rows in 2012-2014 with a missing/zero FTE denominator. Negative dollar values are legitimate budget
    adjustments and are preserved.'
tags:
- education
- governor's office of student achievement
- k12_expenditures
servers:
- server: s3_gold
  type: s3
  location: s3://georgia-data-gold/education/k12_expenditures/**/*.parquet
  format: parquet
- server: local_gold
  type: local
  path: data/gold/education/k12_expenditures/**/*.parquet
  format: parquet
schema:
- name: k12_expenditures
  logicalType: object
  physicalType: parquet
  physicalName: k12_expenditures
  description: k12_expenditures fact table, year-partitioned Parquet (split by detail level where present).
  dataGranularityDescription: One row per year, district_code, school_code, expenditure_function (geography
    columns are NULL at higher aggregation levels).
  customProperties:
  - property: sub_topic
    value: gosa
  - property: detail_levels
    value:
    - schools
    - districts
    - states
  - property: default_detail
    value: schools
  - property: grain
    value:
    - year
    - district_code
    - school_code
    - expenditure_function
  - property: key_metric
    value: expenditure_amount
  - property: null_semantics
    value:
      suppressed_to_null: false
      zero_is_real: true
  - property: partition_columns
    value:
    - year
  - property: path_template
    value: education/k12_expenditures/year={year}/{detail}.parquet
  - property: foreign_keys
    value:
    - column: district_code
      target_object: districts
      target_columns:
      - district_code
      attribute_columns:
      - district_name
      - district_census_id
      - district_type
      scope: domain
    - column: school_code
      target_object: schools
      target_columns:
      - district_code
      - school_code
      attribute_columns:
      - school_name
      scope: domain
  - property: example_queries
    value:
    - description: Latest year (2024), schools detail
      query: SELECT * FROM k12_expenditures WHERE year = 2024 LIMIT 100
    - description: District 644 in 2024
      query: SELECT * FROM k12_expenditures WHERE district_code = '644' AND year = 2024 LIMIT 100
    - description: Filter by expenditure_function = instruction
      query: SELECT * FROM k12_expenditures WHERE expenditure_function = 'instruction' AND year = 2024
        LIMIT 100
  properties:
  - name: year
    logicalType: integer
    physicalType: int
    required: true
    description: Ending (spring) calendar year of the school year (e.g. 2024 for 2023-24), parsed from
      the source's SCHOOL_YEAR / LONG_SCHOOL_YEAR column and cross-checked against the filename year.
    examples:
    - 2024
    customProperties:
    - property: column_role
      value: year
    - property: label
      value: Year
    primaryKey: true
    primaryKeyPosition: 1
  - name: district_code
    logicalType: string
    physicalType: string
    required: false
    description: 'GOSA district code (FK to districts dimension): 3-digit zero-padded county/city codes
      or 7-digit compound codes for commission / state charter schools. Codes 850-888 are RESA service
      agencies (typed ''resa'' in the dimension). NULL on state-level rows — the bronze sentinel ''ALL''
      is mapped to NULL.'
    examples:
    - '644'
    relationships:
    - to: districts.district_code
    customProperties:
    - property: column_role
      value: fk_district
    - property: label
      value: District Code
    primaryKey: true
    primaryKeyPosition: 2
  - name: school_code
    logicalType: string
    physicalType: string
    required: false
    description: 4-digit zero-padded GOSA school code (composite FK to schools dimension with district_code).
      NULL on district- and state-level rows (bronze sentinel 'ALL' mapped to NULL). The 2012 file publishes
      a minority of codes as 3-digit; they are zero-padded to match the other years. 2023-2024 'DOE OTHER'
      rows (GNETS programs, alternative schools, Pre-K centers, central offices) carry real institution
      numbers and appear under the school detail level, matching where the pre-2023 files placed the same
      entities.
    examples:
    - '0189'
    relationships:
    - to: schools.district_code
    - to: schools.school_code
    customProperties:
    - property: column_role
      value: fk_school
    - property: label
      value: School Code
    primaryKey: true
    primaryKeyPosition: 3
  - name: expenditure_function
    logicalType: string
    physicalType: string
    required: true
    description: 'Spending function of the expenditure (11 values): instruction, pupil_services, instructional_support,
      school_administration, general_administration, maintenance_and_operations, transportation, media,
      school_food_services, debt_services, and renovation_and_capital_projects. Every district and state
      row set carries all 11 functions in every year (enforced by quality checks; school rows may publish
      partial function sets), so summing expenditure_amount over the 11 functions gives a district''s
      total expenditure. The bronze spelling ''School food Services'' (lowercase food) is an upstream-report
      typo normalized to school_food_services.'
    examples:
    - 'instruction'
    enum:
    - 'debt_services'
    - 'general_administration'
    - 'instruction'
    - 'instructional_support'
    - 'maintenance_and_operations'
    - 'media'
    - 'pupil_services'
    - 'renovation_and_capital_projects'
    - 'school_administration'
    - 'school_food_services'
    - 'transportation'
    customProperties:
    - property: column_role
      value: categorical
    - property: label
      value: Expenditure Function
    - property: short_description
      value: 'Spending function of the expenditure: instruction, pupil_services, transportation, school_administration,
        and 7 more.'
    - property: has_total
      value: false
    - property: key_metric_grain_contributor
      value: true
    - property: value_coverage
      value:
      - value: 'debt_services'
        first_year: 2011
        last_year: 2024
        rows: 43706
      - value: 'general_administration'
        first_year: 2011
        last_year: 2024
        rows: 43706
      - value: 'instruction'
        first_year: 2011
        last_year: 2024
        rows: 43706
      - value: 'instructional_support'
        first_year: 2011
        last_year: 2024
        rows: 43706
      - value: 'maintenance_and_operations'
        first_year: 2011
        last_year: 2024
        rows: 43706
      - value: 'media'
        first_year: 2011
        last_year: 2024
        rows: 43706
      - value: 'pupil_services'
        first_year: 2011
        last_year: 2024
        rows: 43706
      - value: 'renovation_and_capital_projects'
        first_year: 2011
        last_year: 2024
        rows: 43706
      - value: 'school_administration'
        first_year: 2011
        last_year: 2024
        rows: 43706
      - value: 'school_food_services'
        first_year: 2011
        last_year: 2024
        rows: 43706
      - value: 'transportation'
        first_year: 2011
        last_year: 2024
        rows: 43706
    primaryKey: true
    primaryKeyPosition: 4
  - name: expenditure_amount
    logicalType: number
    physicalType: double
    required: false
    description: Total dollar amount spent on this expenditure function at the row's detail level. Never
      NULL (enforced by a quality check — the source publishes no suppression). Negative values are legitimate
      budget adjustments / corrections and are preserved; no range check applies. This is the full-ledger
      figure; it is NOT comparable to the FESR-methodology totals in the per_pupil_expenditure topic,
      which count only included costs.
    examples:
    - 3960000.0
    customProperties:
    - property: column_role
      value: metric
    - property: label
      value: Expenditure Amount
    - property: short_description
      value: Total dollars spent on this function at the row's level; negatives are real budget adjustments.
    - property: unit
      value: currency
    - property: key_metric
      value: true
  - name: expenditure_per_pupil
    logicalType: number
    physicalType: double
    required: false
    description: 'Per-FTE (full-time-equivalent student) dollar amount spent on this function. Can be
      negative (budget adjustments). NULL only in 2012-2014 where the bronze FTE denominator was missing
      or zero (475 rows; enforced by a quality check). Caution — not always a true per-pupil amount: in
      the 2024 file the source publishes expenditure_per_pupil equal to expenditure_amount verbatim on
      EVERY school-detail row (all 35,398 rows, traditional schools included), so 2024 school-level values
      carry no per-pupil information at all; in 2023 the same artifact covers all 9,526 DOE OTHER school
      rows plus 151 other nonzero school rows; in 2012 it covers the rows with a missing FTE_COUNT; and
      it frequently equals expenditure_amount on aggregate / non-school rows in other years. Standard-district
      and state rows in 2023-2024 do carry true per-FTE values (the 16 RESA rollup district rows, codes
      850-888, do not). For a reliable school-level per-pupil spending series, use the per_pupil_expenditure
      topic (FESR methodology) instead; here, verify expenditure_per_pupil <> expenditure_amount (or sanity-check
      the magnitude) before treating a school-level value as per-pupil spending.'
    examples:
    - 6696.37
    customProperties:
    - property: column_role
      value: metric
    - property: label
      value: Expenditure per Pupil
    - property: unit
      value: currency
    - property: null_meaning
      value: NULL = the entity has no FTE denominator (district central offices, zero-FTE programs) in
        the 2012-2014 source files — the only years whose bronze carried an FTE_COUNT column (475 rows),
        NOT suppression.
  quality:
  - type: sql
    name: non_empty
    description: The dataset has at least one row.
    dimension: completeness
    query: SELECT COUNT(*) FROM {object}
    mustBeGreaterThan: 0
  - type: sql
    name: expenditure_function_in_allowed_values
    description: expenditure_function is one of the 11 canonical codes.
    dimension: conformity
    query: SELECT COUNT(*) FROM {object} WHERE expenditure_function IS NOT NULL AND expenditure_function
      NOT IN ('debt_services', 'general_administration', 'instruction', 'instructional_support', 'maintenance_and_operations',
      'media', 'pupil_services', 'renovation_and_capital_projects', 'school_administration', 'school_food_services',
      'transportation')
    mustBe: 0
  - type: sql
    name: state_rows_complete_every_year
    description: Every year publishes exactly 11 state rows — one per expenditure_function value, covering
      all 11 functions (verified across all 14 bronze files). State rows self-scope via NULL geography
      keys.
    dimension: completeness
    query: SELECT COUNT(*) FROM (SELECT year FROM {object} WHERE district_code IS NULL AND school_code
      IS NULL GROUP BY year HAVING COUNT(*) <> 11 OR COUNT(DISTINCT expenditure_function) <> 11) AS bad_years
    mustBe: 0
  - type: sql
    name: district_entities_carry_all_11_functions
    description: Every district-detail entity (districts, RESA rollups, charters) carries exactly 11 rows
      covering all 11 expenditure_function values in every year it appears (verified across all 14 bronze
      files; the retired combined topic's 17-pair check restricted to the expenditure family). School-detail
      entities do NOT share this property (some publish partial function sets), so the check scopes to
      district rows via geography NULL-ness.
    dimension: completeness
    query: SELECT COUNT(*) FROM (SELECT year, district_code FROM {object} WHERE district_code IS NOT NULL
      AND school_code IS NULL GROUP BY year, district_code HAVING COUNT(*) <> 11 OR COUNT(DISTINCT expenditure_function)
      <> 11) AS bad_entities
    mustBe: 0
  - type: sql
    name: expenditure_amount_never_null
    description: 'The source publishes no suppression and no missing dollar amounts: the total-dollars
      column is populated on every row of all 14 bronze files (verified). A NULL means a parsing regression
      or a new suppression regime — either must be analyzed, not silently passed.'
    dimension: completeness
    query: SELECT COUNT(*) FROM {object} WHERE expenditure_amount IS NULL
    mustBe: 0
  - type: sql
    name: expenditure_per_pupil_null_only_in_2012_2014
    description: 'expenditure_per_pupil NULLs are confined to 2012-2014 — the only years whose bronze
      carried an FTE_COUNT column (475 rows with a missing/zero FTE denominator: 13 in 2012, 286 in 2013,
      176 in 2014). Every other year is fully populated (verified). A NULL elsewhere means a parsing regression
      or a source change.'
    dimension: completeness
    query: SELECT COUNT(*) FROM {object} WHERE expenditure_per_pupil IS NULL AND year NOT IN (2012, 2013,
      2014)
    mustBe: 0
customProperties:
- property: title
  value: K-12 Expenditures by Spending Function
- property: summary
  value: K-12 dollars spent by Georgia public schools and districts, broken out by spending function (instruction,
    transportation, administration, ...), 2011-2024.
- property: source
  value: Governor's Office of Student Achievement (GOSA)
- property: source_url
  value: https://gosa.georgia.gov/dashboards-data-report-card/downloadable-data
- property: update_frequency
  value: annual
- property: year_range
  value: 2011-2024
- property: schema_hash
  value: f87003f1c86b66a23d9bbff2d9be52e9cc0d51ab3dc8843636c6532f81ebee6c
- property: available_years
  value:
  - 2011
  - 2012
  - 2013
  - 2014
  - 2015
  - 2016
  - 2017
  - 2018
  - 2019
  - 2020
  - 2021
  - 2022
  - 2023
  - 2024
- property: year_gaps
  value: []
