Skip to content

Add functionality to track FCA Call Report Schedule Metadata - #30

Open
RNKuhns wants to merge 11 commits into
mainfrom
fca_schedule_metadata
Open

Add functionality to track FCA Call Report Schedule Metadata#30
RNKuhns wants to merge 11 commits into
mainfrom
fca_schedule_metadata

Conversation

@RNKuhns

@RNKuhns RNKuhns commented Aug 3, 2026

Copy link
Copy Markdown
Member

No description provided.

RNKuhns and others added 6 commits August 2, 2026 22:29
…ation

Real FCA data shows a field's definition can change while it stays
continuously present (RCB's INV_CODE embeds a code list that grows over
time with no presence gap) -- the previous model (one shared dtype/
definition across a field's whole history) couldn't represent that.
FieldAttributes now holds one or more FieldVersions, each a span where
dtype/definition held constant; adjacent versions are allowed only when
their content actually differs, so FieldSchema.as_of can surface the
version genuinely active at a given quarter instead of always the
latest.

Also add is_equal/compare (with check_order) on FieldSchema and
FileMetadata for inspecting drift between two snapshots, and to_json/
from_json -- the format canonical FCA schedule metadata will ship in,
chosen over the existing dataframe round-trip because a field's versions
nest naturally under its name.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
FCA layout files only ever say "Numeric"/"Alphanum." plus a decimal
position -- nothing in the codebase previously translated that into a
narwhals dtype (fca/reader.py's value-level casting infers Python int/
float/str ad hoc per raw value, never producing or consulting a stored
dtype). The schedule-metadata generation script needs this translation
to build FieldVersion.dtype, so it's added here as its own small,
tested, doctested function rather than inlined into the script.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Maintainer tool (not part of the package's runtime or its coverage gate):
scans every real, checked-in FCA archive to incrementally build each
schedule's canonical FieldMetadata, applies a small hand-maintained
overrides file for SME corrections, and writes the merged, authoritative
result to src/call_report/fca/data/ for the package to ship.

Incremental by default -- FCA's archived quarters never change once
published, so only newly added periods need reprocessing; --full forces
a from-scratch rebuild and audits it against the previous base via the
new FieldSchema.compare, catching any drift a bug would introduce.

Also add a codespell ignore-words-list entry for "SME" (subject-matter
expert), a real recurring term here that reads as a typo for "some".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
get_fca_file_metadata/get_institutions_file_metadata/all_fca_file_metadata
read the JSON files scripts/generate_fca_schedule_metadata.py ships under
src/call_report/fca/data/schedules/ via importlib.resources (new to this
codebase -- no prior use of it), cached per schedule so a file parses at
most once per process. Verified against a real built wheel installed
into an isolated venv, not just the editable dev install.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
api_reference.rst was missing entries for FieldAttributes/FieldSchema/
FileMetadata entirely (a pre-existing gap) -- fixed while adding the new
FieldVersion/FieldChange/FieldSchemaDiff/FileMetadataDiff and fca schedule-
metadata loaders alongside them. CLAUDE.md's repo layout now documents
the new src/call_report/fca/data/schedules/ (shipped), data/fca-schedule-
metadata/{base,overrides}/ (not shipped, generation working data), and
scripts/generate_fca_schedule_metadata.py.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Shows the new schedule-metadata API (get_fca_file_metadata) alongside
the existing data-loading quickstart, using RCF1 specifically because its
LOANSTATUS field demonstrates both a real code column (its metadata
definition documents what the loaded integer codes mean) and real
cross-time drift (FCA revised its code list in 2015 with no presence
gap, so it has two versions -- as_of recovers whichever definition
applied at a given quarter). Every value shown is verified against a
real run, not illustrative placeholder output.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@RNKuhns RNKuhns changed the title Add a Get Started example comparing RCF1's metadata to its loaded data Add functionality to track FCA Call Report Schedule Metadata Aug 3, 2026
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (3fb4e67) to head (c6fc957).

Additional details and impacted files
@@            Coverage Diff             @@
##              main       #30    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files           19        20     +1     
  Lines         1146      1269   +123     
  Branches       125       137    +12     
==========================================
+ Hits          1146      1269   +123     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

RNKuhns added 3 commits August 2, 2026 23:30
Melts each loaded schedule to a long intermediate, tags code-bearing
fields distinctly from plain ones, then pivots on (UNINUM, period) so
every schedule's variables land in one row per institution per quarter.
Works on every configured backend, including pyarrow -- core._backend.pivot
falls back to a manual filter-and-join reshape where narwhals has no
native pivot support for it.

Part 1 of 3 for v0.2's wide/long/mini-dataset stacking goal; long-format
and shared-dimension mini-datasets are follow-on work.
"""Pivot takes no positional arguments."""
frame = _build_long_frame(backend="pandas")
with pytest.raises(TypeError):
pivot(frame, "key", ["UNINUM", "period"], "value") # type: ignore[call-arg]
Comment thread tests/fca/test_reshape.py
with config_context(dataframe_backend="pandas"):
frame = build_frame(data={"UNINUM": [1], "period": ["2026-03-31"], "A": [1]})
with pytest.raises(TypeError):
melt_schedule_frame(frame, "RC", None) # type: ignore[call-arg]
Comment thread tests/fca/test_reshape.py Fixed
RNKuhns added 2 commits August 4, 2026 22:55
That directory holds raw, uncorrected per-schedule text extracted
verbatim from FCA's own layout files (working data for
scripts/generate_fca_schedule_metadata.py); genuine source typos like
"similiar" and "Recieved" belong in the overrides pipeline, not codespell
suppression. pre-commit's own codespell hook already skips it (scoped to
python/markdown types), but the standalone codespell-annotations job
scans the whole repo unrestricted.
…data

FCACallReport.to_wide_format used to collect each schedule immediately
after loading it, before any melt/concat/pivot work started. With
lazy=True configured, every schedule now stays a narwhals LazyFrame
through melt, concat, and column-key computation -- the only place
collection genuinely happens is core._backend.pivot, since a pivoted
frame's schema depends on the "on" column's distinct values and can't be
determined lazily. FileMetadata.to_dataframe had the same avoidable
early collect; fixed the same way, matching the fresh file-level frame's
laziness to the (possibly already-lazy) field-level frame before
concatenating rather than collecting one of them upfront.

concat() gained a proper overload ladder (eager-in/eager-out,
lazy-in/lazy-out, and a generic fallback for callers that don't
statically know which) so precise callers keep precise return types, and
its schema comparisons switched from `.columns` to `.collect_schema()`
to avoid a PerformanceWarning on lazy input.
Comment thread tests/fca/test_reshape.py
with config_context(dataframe_backend="pandas"):
rc = build_frame(data={"UNINUM": [1], "period": ["2026-03-31"], "A": [1]})
with pytest.raises(TypeError):
to_wide_format({"RC": rc}, {"RC": None}, {"RC": ()}) # type: ignore[call-arg]
) -> nw.DataFrame[Any]:
"""Stack multiple eager narwhals frames according to a schema policy.
) -> nw.DataFrame[Any]: # numpydoc ignore=GL08
... # pragma: no cover
def concat(
*, frames: Sequence[nw.LazyFrame[Any]], how: SchemaPolicy
) -> nw.LazyFrame[Any]: # numpydoc ignore=GL08
... # pragma: no cover
def concat(
*, frames: Sequence[FrameOrLazy], how: SchemaPolicy
) -> FrameOrLazy: # numpydoc ignore=GL08
... # pragma: no cover
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant