← Files CA Property Tax Appeal GuideARCHIVED FILE

skills/ca-property-tax-appeal/scripts/ca_appeal_checks.py

15.7 KB · Oct 5, 2026 · 18:34 UTC

↓ Download file

#!/usr/bin/env python3
"""Deterministic checks for a California residential assessment-appeal evidence file.

Usage:
  python3 ca_appeal_checks.py evidence.json

Required JSON fields:
  state, county, assessment_year, appeal_type, valuation_date,
  current_assessed_value, proposed_value, deadline, deadline_source,
  deadline_source_class, as_of_date, comparables

Optional:
  ad_valorem_rate (decimal, e.g. 0.0112), ad_valorem_rate_source,
  ad_valorem_rate_source_class, fixed_charges, factored_base_year_value,
  filing_fee, payment_service_fee, fee_source, fee_source_class,
  fee_nonrefundable, parcel_count, other_unavoidable_costs,
  card_service_fee_percent, card_service_fee_minimum, payment_method,
  evidence_confidence

Each comparable requires:
  address, sale_date, sale_price, living_area, source_url,
  source_class, arm_length_verified
"""

from __future__ import annotations

import argparse
import json
import sys
from datetime import date, timedelta
from pathlib import Path
from typing import Any


ALLOWED_APPEAL_TYPES = {
    "decline_in_value",
    "base_year_or_supplemental",
    "escape_assessment",
    "calamity",
}
ALLOWED_SOURCE_CLASSES = {"official", "primary_document", "secondary", "unverified_lead"}


def parse_iso(value: Any, field: str, errors: list[str]) -> date | None:
    if not isinstance(value, str):
        errors.append(f"{field} must be an ISO date string (YYYY-MM-DD).")
        return None
    try:
        return date.fromisoformat(value)
    except ValueError:
        errors.append(f"{field} is not a valid ISO date: {value!r}.")
        return None


def positive_number(value: Any, field: str, errors: list[str]) -> float | None:
    if isinstance(value, bool) or not isinstance(value, (int, float)) or value <= 0:
        errors.append(f"{field} must be a positive number.")
        return None
    return float(value)


def analyze(payload: dict[str, Any]) -> dict[str, Any]:
    errors: list[str] = []
    warnings: list[str] = []

    state = str(payload.get("state", "")).strip().upper()
    if state not in {"CA", "CALIFORNIA"}:
        errors.append("state must be CA or California for California checks.")

    county = str(payload.get("county", "")).strip()
    if not county:
        errors.append("county is required.")

    year = payload.get("assessment_year")
    if isinstance(year, bool) or not isinstance(year, int) or not 1900 <= year <= 2200:
        errors.append("assessment_year must be a four-digit integer.")

    appeal_type = payload.get("appeal_type")
    if appeal_type not in ALLOWED_APPEAL_TYPES:
        errors.append(f"appeal_type must be one of {sorted(ALLOWED_APPEAL_TYPES)}.")

    valuation_date = parse_iso(payload.get("valuation_date"), "valuation_date", errors)
    deadline = parse_iso(payload.get("deadline"), "deadline", errors)
    as_of = parse_iso(payload.get("as_of_date"), "as_of_date", errors)
    deadline_source = str(payload.get("deadline_source", "")).strip()
    if not deadline_source.startswith("https://"):
        errors.append("deadline_source must be a direct HTTPS official-source URL.")
    if payload.get("deadline_source_class") != "official":
        errors.append("deadline_source_class must be 'official'.")

    current_value = positive_number(payload.get("current_assessed_value"), "current_assessed_value", errors)
    proposed_value = positive_number(payload.get("proposed_value"), "proposed_value", errors)
    if current_value is not None and proposed_value is not None and proposed_value >= current_value:
        warnings.append("proposed_value is not lower than current_assessed_value; no reduction is indicated.")

    if appeal_type == "decline_in_value" and valuation_date is not None:
        expected = date(valuation_date.year, 1, 1)
        if valuation_date != expected:
            errors.append("A California decline_in_value valuation_date must be January 1 of the appeal year.")
        if isinstance(year, int) and valuation_date.year != year:
            errors.append("For decline_in_value, valuation_date year must equal assessment_year.")
        factored_base = payload.get("factored_base_year_value")
        if factored_base is None:
            warnings.append("factored_base_year_value is missing; Proposition 8 eligibility cannot be fully confirmed.")
        else:
            factored_base_value = positive_number(factored_base, "factored_base_year_value", errors)
            if (
                factored_base_value is not None
                and proposed_value is not None
                and proposed_value >= factored_base_value
            ):
                warnings.append("proposed_value is not below factored_base_year_value; Proposition 8 decline-in-value eligibility is not indicated.")

    latest_sale_date = valuation_date + timedelta(days=90) if valuation_date else None
    admissible_comps: list[dict[str, Any]] = []
    verified_comps: list[dict[str, Any]] = []
    excluded_comps: list[dict[str, str]] = []
    comparables = payload.get("comparables")
    if not isinstance(comparables, list):
        errors.append("comparables must be an array.")
        comparables = []

    for index, comp in enumerate(comparables):
        prefix = f"comparables[{index}]"
        if not isinstance(comp, dict):
            errors.append(f"{prefix} must be an object.")
            continue
        address = str(comp.get("address", "")).strip()
        if not address:
            errors.append(f"{prefix}.address is required.")
        sale_date = parse_iso(comp.get("sale_date"), f"{prefix}.sale_date", errors)
        price = positive_number(comp.get("sale_price"), f"{prefix}.sale_price", errors)
        area = positive_number(comp.get("living_area"), f"{prefix}.living_area", errors)
        source_url = str(comp.get("source_url", "")).strip()
        if not source_url.startswith("https://"):
            errors.append(f"{prefix}.source_url must be a direct HTTPS URL.")
        source_class = comp.get("source_class")
        if source_class not in ALLOWED_SOURCE_CLASSES:
            errors.append(f"{prefix}.source_class must be one of {sorted(ALLOWED_SOURCE_CLASSES)}.")
        if comp.get("arm_length_verified") is not True:
            warnings.append(f"{prefix} is not verified as arm's-length.")

        if sale_date and latest_sale_date and sale_date > latest_sale_date:
            excluded_comps.append({
                "address": address or prefix,
                "reason": f"sale date {sale_date.isoformat()} is more than 90 days after valuation date",
            })
            continue

        if sale_date and price and area and address and source_url.startswith("https://"):
            result_comp = {
                "address": address,
                "sale_date": sale_date.isoformat(),
                "sale_price": price,
                "living_area": area,
                "unadjusted_price_per_sqft": round(price / area, 2),
                "source_class": source_class,
                "verified_for_confidence_count": (
                    source_class in {"official", "primary_document"}
                    and comp.get("arm_length_verified") is True
                ),
            }
            admissible_comps.append(result_comp)
            if result_comp["verified_for_confidence_count"]:
                verified_comps.append(result_comp)

    if len(verified_comps) < 3:
        warnings.append("Fewer than three official/primary, arm's-length-verified, date-admissible comparables remain; evidence confidence is insufficient or weak.")

    tax_impact = None
    cost_benefit = None
    fixed_charges = payload.get("fixed_charges", 0)
    if (
        isinstance(fixed_charges, bool)
        or not isinstance(fixed_charges, (int, float))
        or fixed_charges < 0
    ):
        errors.append("fixed_charges must be a non-negative number when supplied.")
        fixed_charges = None
    rate = payload.get("ad_valorem_rate")
    if rate is not None:
        rate_source = str(payload.get("ad_valorem_rate_source", "")).strip()
        if not rate_source.startswith("https://"):
            errors.append("ad_valorem_rate_source must be a direct HTTPS official-source URL when ad_valorem_rate is supplied.")
        if payload.get("ad_valorem_rate_source_class") != "official":
            errors.append("ad_valorem_rate_source_class must be 'official' when ad_valorem_rate is supplied.")
        if isinstance(rate, bool) or not isinstance(rate, (int, float)) or not 0 < rate < 0.1:
            errors.append("ad_valorem_rate must be a decimal greater than 0 and less than 0.1 (for example 0.0112).")
        elif current_value is not None and proposed_value is not None:
            reduction = max(current_value - proposed_value, 0.0)
            tax_impact = {
                "assessed_value_reduction": round(reduction, 2),
                "ad_valorem_rate": float(rate),
                "estimated_variable_tax_reduction": round(reduction * float(rate), 2),
                "fixed_charges_assumed_unchanged": round(float(fixed_charges), 2) if fixed_charges is not None else None,
                "fixed_charges_included": False,
            }

    filing_fee = payload.get("filing_fee")
    payment_service_fee = payload.get("payment_service_fee", 0)
    parcel_count = payload.get("parcel_count", 1)
    other_costs = payload.get("other_unavoidable_costs", 0)
    if isinstance(parcel_count, bool) or not isinstance(parcel_count, int) or parcel_count < 1:
        errors.append("parcel_count must be a positive integer when supplied.")
    if isinstance(other_costs, bool) or not isinstance(other_costs, (int, float)) or other_costs < 0:
        errors.append("other_unavoidable_costs must be a non-negative number when supplied.")
    if filing_fee is not None:
        if (
            isinstance(filing_fee, bool)
            or not isinstance(filing_fee, (int, float))
            or filing_fee < 0
        ):
            errors.append("filing_fee must be a non-negative number when supplied.")
        if (
            isinstance(payment_service_fee, bool)
            or not isinstance(payment_service_fee, (int, float))
            or payment_service_fee < 0
        ):
            errors.append("payment_service_fee must be a non-negative number when supplied.")
        fee_source = str(payload.get("fee_source", "")).strip()
        if not fee_source.startswith("https://"):
            errors.append("fee_source must be a direct HTTPS official-source URL when filing_fee is supplied.")
        if payload.get("fee_source_class") != "official":
            errors.append("fee_source_class must be 'official' when filing_fee is supplied.")
        if not isinstance(payload.get("fee_nonrefundable"), bool):
            errors.append("fee_nonrefundable must be true or false when filing_fee is supplied.")
        percent = payload.get("card_service_fee_percent")
        minimum = payload.get("card_service_fee_minimum", 0)
        if percent is not None:
            if isinstance(percent, bool) or not isinstance(percent, (int, float)) or percent < 0 or percent >= 100:
                errors.append("card_service_fee_percent must be a percentage from 0 up to 100.")
            elif (
                payload.get("payment_method") == "credit_card"
                and isinstance(filing_fee, (int, float))
                and not isinstance(filing_fee, bool)
                and isinstance(parcel_count, int)
                and not isinstance(parcel_count, bool)
                and parcel_count >= 1
            ):
                if isinstance(minimum, bool) or not isinstance(minimum, (int, float)) or minimum < 0:
                    errors.append("card_service_fee_minimum must be non-negative.")
                else:
                    payment_service_fee = max(float(filing_fee) * int(parcel_count) * float(percent) / 100, float(minimum))
        if (
            tax_impact is not None
            and isinstance(filing_fee, (int, float))
            and not isinstance(filing_fee, bool)
            and filing_fee >= 0
            and isinstance(payment_service_fee, (int, float))
            and not isinstance(payment_service_fee, bool)
            and payment_service_fee >= 0
            and isinstance(parcel_count, int)
            and not isinstance(parcel_count, bool)
            and parcel_count >= 1
            and isinstance(other_costs, (int, float))
            and not isinstance(other_costs, bool)
            and other_costs >= 0
        ):
            total_fees = float(filing_fee) * int(parcel_count) + float(payment_service_fee) + float(other_costs)
            annual_reduction = tax_impact["estimated_variable_tax_reduction"]
            net = annual_reduction - total_fees
            close_threshold = max(25.0, total_fees * 0.10)
            confidence = payload.get("evidence_confidence")
            if confidence in {"weak", "insufficient"}:
                screen = "cannot_determine"
            elif abs(net) <= close_threshold:
                screen = "close_call"
            elif net > 0:
                screen = "financially_favorable"
            else:
                screen = "financially_unfavorable"
            cost_benefit = {
                "estimated_one_year_variable_tax_reduction": annual_reduction,
                "verified_unavoidable_fees": round(total_fees, 2),
                "estimated_first_year_net_before_other_costs": round(net, 2),
                "break_even_assessed_value_reduction": round(total_fees / float(rate), 2),
                "parcel_count": int(parcel_count),
                "payment_service_fee": round(float(payment_service_fee), 2),
                "screen": screen,
                "not_probability_adjusted": True,
                "future_years_not_guaranteed": True,
            }

    if cost_benefit is None:
        missing_cost_inputs = []
        if tax_impact is None:
            missing_cost_inputs.append("supported value reduction and official ad valorem rate")
        if filing_fee is None:
            missing_cost_inputs.append("verified filing fee")
        cost_benefit = {"screen": "cannot_determine", "missing_inputs": missing_cost_inputs}

    deadline_status = None
    if deadline and as_of:
        days = (deadline - as_of).days
        deadline_status = {
            "days_remaining": days,
            "status": "expired" if days < 0 else "due_today" if days == 0 else "open",
        }
        if days < 0:
            warnings.append("The supplied formal filing deadline has passed; do not present the regular appeal as timely.")
        elif days <= 14:
            warnings.append("The supplied formal filing deadline is within 14 days; lead with filing protection.")

    return {
        "valid": not errors,
        "errors": errors,
        "warnings": warnings,
        "latest_admissible_sale_date": latest_sale_date.isoformat() if latest_sale_date else None,
        "admissible_comparables": admissible_comps,
        "verified_comparables": verified_comps,
        "verified_comparable_count": len(verified_comps),
        "excluded_comparables": excluded_comps,
        "deadline_status": deadline_status,
        "tax_impact": tax_impact,
        "cost_benefit": cost_benefit,
    }


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("evidence_json", type=Path)
    args = parser.parse_args()
    try:
        payload = json.loads(args.evidence_json.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError) as exc:
        print(json.dumps({"valid": False, "errors": [str(exc)]}, indent=2))
        return 2
    if not isinstance(payload, dict):
        print(json.dumps({"valid": False, "errors": ["Root JSON value must be an object."]}, indent=2))
        return 2
    result = analyze(payload)
    print(json.dumps(result, indent=2, sort_keys=True))
    return 0 if result["valid"] else 1


if __name__ == "__main__":
    sys.exit(main())

SHA-256: 7463c01688f10cd2a3e4020f37559bc0e6060fd5e0f0db6f9acb564ed143a918