Coverage report: 88%

Files Functions Classes

coverage.py v7.15.0, created at 2026-07-05 04:36 +0000

File function   statements missing excluded   coverage
src/sankey_cashflow/__init__.py (no function)   9 0 0   100%
src/sankey_cashflow/cli.py build_arg_parser   20 0 0   100%
src/sankey_cashflow/cli.py _prompt_for_date_range   18 18 0   0%
src/sankey_cashflow/cli.py main   38 6 0   84%
src/sankey_cashflow/cli.py (no function)   14 1 0   93%
src/sankey_cashflow/data_row.py DataRow.validate   30 1 0   97%
src/sankey_cashflow/data_row.py DataRow.create   25 7 0   72%
src/sankey_cashflow/data_row.py DataRow.tag_matches   6 0 0   100%
src/sankey_cashflow/data_row.py (no function)   10 0 0   100%
src/sankey_cashflow/diagram.py _hover_breakdown   11 0 0   100%
src/sankey_cashflow/diagram.py build_sankey_figure   18 0 0   100%
src/sankey_cashflow/diagram.py _sum_row_amount   7 2 0   71%
src/sankey_cashflow/diagram.py build_line_figure   22 1 0   95%
src/sankey_cashflow/diagram.py (no function)   6 0 0   100%
src/sankey_cashflow/io.py fetch_data   25 7 0   72%
src/sankey_cashflow/io.py fetch_data.data_source_router   49 43 0   12%
src/sankey_cashflow/io.py read_csv_as_df   14 1 0   93%
src/sankey_cashflow/io.py read_gsheet_as_df   10 10 0   0%
src/sankey_cashflow/io.py (no function)   11 0 0   100%
src/sankey_cashflow/labels.py RowLabels.__init__   34 0 0   100%
src/sankey_cashflow/labels.py RowLabels.data   1 0 0   100%
src/sankey_cashflow/labels.py RowLabels.graph   1 1 0   0%
src/sankey_cashflow/labels.py RowLabels.get_longest_path   1 0 0   100%
src/sankey_cashflow/labels.py RowLabels.get_path   6 1 0   83%
src/sankey_cashflow/labels.py RowLabels.get_label   14 3 0   79%
src/sankey_cashflow/labels.py RowLabels.get_attribute   23 0 0   100%
src/sankey_cashflow/labels.py RowLabels.print_graph   5 0 0   100%
src/sankey_cashflow/labels.py (no function)   15 0 0   100%
src/sankey_cashflow/settings.py AppSettings.__init__   53 1 0   98%
src/sankey_cashflow/settings.py AppSettings.date_filter_start   1 0 0   100%
src/sankey_cashflow/settings.py AppSettings.date_filter_start   3 0 0   100%
src/sankey_cashflow/settings.py AppSettings.date_filter_end   1 0 0   100%
src/sankey_cashflow/settings.py AppSettings.date_filter_end   3 0 0   100%
src/sankey_cashflow/settings.py AppSettings.g_creds   1 0 0   100%
src/sankey_cashflow/settings.py AppSettings.g_creds   3 1 0   67%
src/sankey_cashflow/settings.py AppSettings.labels_source   1 0 0   100%
src/sankey_cashflow/settings.py AppSettings.labels_source   3 0 0   100%
src/sankey_cashflow/settings.py AppSettings.source_data_location   3 0 0   100%
src/sankey_cashflow/settings.py AppSettings.validate_sources   20 4 0   80%
src/sankey_cashflow/settings.py (no function)   25 0 0   100%
src/sankey_cashflow/transactions.py Transactions.__init__   21 3 0   86%
src/sankey_cashflow/transactions.py Transactions.grouped_data   1 0 0   100%
src/sankey_cashflow/transactions.py Transactions.processed_data   1 0 0   100%
src/sankey_cashflow/transactions.py Transactions._validate_df   8 1 0   88%
src/sankey_cashflow/transactions.py Transactions.audit   25 9 0   64%
src/sankey_cashflow/transactions.py Transactions.audit.safe_sum   2 0 0   100%
src/sankey_cashflow/transactions.py Transactions.audit.safe_sum.vval   3 1 0   67%
src/sankey_cashflow/transactions.py Transactions.process   30 17 0   43%
src/sankey_cashflow/transactions.py Transactions.process_line   26 18 0   31%
src/sankey_cashflow/transactions.py Transactions.filter_tags   16 1 0   94%
src/sankey_cashflow/transactions.py Transactions.add_row   9 4 0   56%
src/sankey_cashflow/transactions.py Transactions.apply_labels   89 21 0   76%
src/sankey_cashflow/transactions.py Transactions.apply_labels.get_source_target_labels   24 8 0   67%
src/sankey_cashflow/transactions.py Transactions.process_rows   83 21 0   75%
src/sankey_cashflow/transactions.py Transactions.collapse   8 2 0   75%
src/sankey_cashflow/transactions.py Transactions.create_surplus_deficit_flows   34 6 0   82%
src/sankey_cashflow/transactions.py Transactions.filter_dates   29 4 0   86%
src/sankey_cashflow/transactions.py Transactions.explode_tags   6 6 0   0%
src/sankey_cashflow/transactions.py Transactions.distribute_amounts   40 0 0   100%
src/sankey_cashflow/transactions.py Transactions.update_title   9 4 0   56%
src/sankey_cashflow/transactions.py (no function)   28 0 0   100%
src/sankey_cashflow/utils.py is_null   14 3 0   79%
src/sankey_cashflow/utils.py is_empty   18 2 0   89%
src/sankey_cashflow/utils.py df_date_filter   11 0 0   100%
src/sankey_cashflow/utils.py save_report   6 6 0   0%
src/sankey_cashflow/utils.py validate_date_string   26 0 0   100%
src/sankey_cashflow/utils.py normalize_amounts   11 0 0   100%
src/sankey_cashflow/utils.py (no function)   18 0 0   100%
tests/conftest.py Args.__init__   4 0 0   100%
tests/conftest.py make_args   1 0 0   100%
tests/conftest.py default_app_settings   1 0 0   100%
tests/conftest.py sample_labels_data   1 0 0   100%
tests/conftest.py sample_row_labels   1 0 0   100%
tests/conftest.py sample_transactions_df   2 0 0   100%
tests/conftest.py sample_transactions   1 0 0   100%
tests/conftest.py make_transactions_df   2 0 0   100%
tests/conftest.py make_transactions_df._make   7 0 0   100%
tests/conftest.py (no function)   20 0 0   100%
tests/test_app_settings.py TestDefaultInitialization.test_default_initialization   26 0 0   100%
tests/test_app_settings.py TestDefaultInitialization.test_verbose_logging   2 0 0   100%
tests/test_app_settings.py TestHoverOption.test_hover_maps_to_description   2 0 0   100%
tests/test_app_settings.py TestHoverOption.test_hover_disabled   2 0 0   100%
tests/test_app_settings.py TestHoverOption.test_hover_unrecognized_keeps_default   2 0 0   100%
tests/test_app_settings.py TestDiagramType.test_recognized_dtype   2 0 0   100%
tests/test_app_settings.py TestDiagramType.test_unrecognized_dtype_keeps_default   2 0 0   100%
tests/test_app_settings.py TestTagsStoresExclude.test_tags_split_and_stripped   2 0 0   100%
tests/test_app_settings.py TestTagsStoresExclude.test_tag_override_requires_tags   2 0 0   100%
tests/test_app_settings.py TestTagsStoresExclude.test_tag_override_ignored_without_tags   2 0 0   100%
tests/test_app_settings.py TestTagsStoresExclude.test_feed_in_requires_tags   2 0 0   100%
tests/test_app_settings.py TestTagsStoresExclude.test_feed_in_ignored_without_tags   2 0 0   100%
tests/test_app_settings.py TestTagsStoresExclude.test_exclude_split_and_stripped   2 0 0   100%
tests/test_app_settings.py TestTagsStoresExclude.test_stores_split_and_stripped   2 0 0   100%
tests/test_app_settings.py TestTagsStoresExclude.test_tags_and_stores_together_raises   2 0 0   100%
tests/test_app_settings.py TestDateFilterSetters.test_date_filter_start_setter   2 0 0   100%
tests/test_app_settings.py TestDateFilterSetters.test_date_filter_start_setter_empty_is_none   2 0 0   100%
tests/test_app_settings.py TestDateFilterSetters.test_date_filter_end_setter   2 0 0   100%
tests/test_app_settings.py TestDateFilterSetters.test_date_filter_end_setter_empty_is_none   2 0 0   100%
tests/test_app_settings.py TestCredsAndLabelsSetters.test_g_creds_setter_missing_file_raises   2 0 0   100%
tests/test_app_settings.py TestCredsAndLabelsSetters.test_labels_source_setter_missing_file_raises   2 0 0   100%
tests/test_app_settings.py TestCredsAndLabelsSetters.test_labels_source_setter_valid_csv   2 0 0   100%
tests/test_app_settings.py TestSourceValidation.test_source_data_location_csv   1 0 0   100%
tests/test_app_settings.py TestSourceValidation.test_source_data_location_gsheet   2 0 0   100%
tests/test_app_settings.py TestSourceValidation.test_validate_sources_missing_source_raises   3 0 0   100%
tests/test_app_settings.py TestSourceValidation.test_validate_sources_csv_requires_csv_labels   2 0 0   100%
tests/test_app_settings.py TestSourceValidation.test_validate_sources_missing_csv_file_raises   2 0 0   100%
tests/test_app_settings.py TestSourceValidation.test_validate_sources_gsheet_requires_sheet_name   2 0 0   100%
tests/test_app_settings.py TestSourceValidation.test_validate_sources_gsheet_requires_creds_file   2 0 0   100%
tests/test_app_settings.py (no function)   42 0 0   100%
tests/test_cli.py TestBuildArgParser.test_defaults   19 0 0   100%
tests/test_cli.py TestBuildArgParser.test_parses_source_and_srcmap   3 0 0   100%
tests/test_cli.py TestBuildArgParser.test_parses_store_true_flags   10 0 0   100%
tests/test_cli.py TestBuildArgParser.test_parses_string_options   8 0 0   100%
tests/test_cli.py TestMainEndToEnd.test_main_runs_sankey_pipeline   5 0 0   100%
tests/test_cli.py TestMainEndToEnd.test_main_runs_line_pipeline   6 0 0   100%
tests/test_cli.py TestMainEndToEnd.test_main_audit_mode_exits_without_generating_diagram   9 0 0   100%
tests/test_cli.py TestMainEndToEnd.test_main_invalid_source_raises   2 0 0   100%
tests/test_cli.py (no function)   13 0 0   100%
tests/test_data_loading.py TestReadCsvAsDf.test_reads_single_csv   3 0 0   100%
tests/test_data_loading.py TestReadCsvAsDf.test_concatenates_multiple_csvs   8 0 0   100%
tests/test_data_loading.py TestReadCsvAsDf.test_missing_file_raises   2 0 0   100%
tests/test_data_loading.py TestReadCsvAsDf.test_non_csv_file_raises   4 0 0   100%
tests/test_data_loading.py TestFetchData.test_loads_sample_labels_and_transactions   5 0 0   100%
tests/test_data_loading.py TestFetchData.test_amount_column_normalized_to_float   3 0 0   100%
tests/test_data_loading.py TestFetchData.test_header_matches_data_row_schema   3 0 0   100%
tests/test_data_loading.py (no function)   11 0 0   100%
tests/test_data_row.py valid_row   1 0 0   100%
tests/test_data_row.py TestValidate.test_validate_valid_data   1 0 0   100%
tests/test_data_row.py TestValidate.test_validate_invalid_amount_raises   4 0 0   100%
tests/test_data_row.py TestValidate.test_validate_non_nullable_field_raises   4 0 0   100%
tests/test_data_row.py TestValidate.test_validate_wrong_column_count_raises   2 0 0   100%
tests/test_data_row.py TestValidate.test_validate_disallowed_type_value_raises   4 0 0   100%
tests/test_data_row.py TestValidate.test_validate_header_only_correct_order   4 0 0   100%
tests/test_data_row.py TestValidate.test_validate_header_only_wrong_order   4 0 0   100%
tests/test_data_row.py TestCreate.test_create_matches_manually_built_row   2 0 0   100%
tests/test_data_row.py TestCreate.test_create_coerces_string_amount   2 0 0   100%
tests/test_data_row.py TestCreate.test_create_unparseable_amount_defaults_to_zero   2 0 0   100%
tests/test_data_row.py TestCreate.test_create_null_tips_defaults_to_zero   2 0 0   100%
tests/test_data_row.py TestCreate.test_create_null_distribution_defaults_to_zero   2 0 0   100%
tests/test_data_row.py TestCreate.test_create_null_sales_tax_defaults_to_zero   2 0 0   100%
tests/test_data_row.py TestTagMatches.test_returns_matching_tags   1 0 0   100%
tests/test_data_row.py TestTagMatches.test_no_matches_returns_empty_list   1 0 0   100%
tests/test_data_row.py TestTagMatches.test_no_search_tags_returns_none   1 0 0   100%
tests/test_data_row.py TestTagMatches.test_no_row_tags_returns_none   1 0 0   100%
tests/test_data_row.py (no function)   25 0 0   100%
tests/test_diagram.py _process   8 0 0   100%
tests/test_diagram.py processed_transactions   1 0 0   100%
tests/test_diagram.py TestBuildSankeyFigure.test_returns_figure   3 0 0   100%
tests/test_diagram.py TestBuildSankeyFigure.test_links_match_grouped_data_length   3 0 0   100%
tests/test_diagram.py TestBuildSankeyFigure.test_title_uses_transactions_title   3 0 0   100%
tests/test_diagram.py TestBuildSankeyFigure.test_safe_to_call_more_than_once   5 0 0   100%
tests/test_diagram.py TestBuildSankeyFigure.test_hover_customdata_present_when_enabled   3 0 0   100%
tests/test_diagram.py TestBuildSankeyFigure.test_no_hover_customdata_when_disabled   4 0 0   100%
tests/test_diagram.py TestBuildLineFigure.test_returns_figure_with_traces   5 0 0   100%
tests/test_diagram.py TestBuildLineFigure.test_excludes_income_and_uncategorized   6 0 0   100%
tests/test_diagram.py TestBuildLineFigure.test_excludes_x_prefixed_classifications   6 0 0   100%
tests/test_diagram.py TestBuildLineFigure.test_does_not_mutate_processed_data   5 0 0   100%
tests/test_diagram.py (no function)   18 0 0   100%
tests/test_helpers.py TestIsNull.test_null_values   1 0 0   100%
tests/test_helpers.py TestIsNull.test_non_null_values   1 0 0   100%
tests/test_helpers.py TestIsEmpty.test_empty_values   1 0 0   100%
tests/test_helpers.py TestIsEmpty.test_non_empty_values   1 0 0   100%
tests/test_helpers.py TestIsEmpty.test_zero_is_empty_when_nonzero_flag_set   2 0 0   100%
tests/test_helpers.py TestIsEmpty.test_nonzero_number_not_empty_with_flag   2 0 0   100%
tests/test_helpers.py TestIsEmpty.test_nonzero_flag_does_not_affect_non_numeric   1 0 0   100%
tests/test_helpers.py TestValidateDateString.test_valid_yyyy_mm_dd   1 0 0   100%
tests/test_helpers.py TestValidateDateString.test_valid_mm_dd_yyyy   1 0 0   100%
tests/test_helpers.py TestValidateDateString.test_out_of_range_year   1 0 0   100%
tests/test_helpers.py TestValidateDateString.test_invalid_month   1 0 0   100%
tests/test_helpers.py TestValidateDateString.test_invalid_day   1 0 0   100%
tests/test_helpers.py TestValidateDateString.test_unrecognized_format   1 0 0   100%
tests/test_helpers.py TestValidateDateString.test_allow_empty_true   2 0 0   100%
tests/test_helpers.py TestValidateDateString.test_allow_empty_false_raises_on_empty_string   2 0 0   100%
tests/test_helpers.py TestValidateDateString.test_allow_empty_false_raises_on_none   2 0 0   100%
tests/test_helpers.py TestNormalizeAmounts.test_dollar_and_comma_stripped   3 0 0   100%
tests/test_helpers.py TestNormalizeAmounts.test_plain_float_string_converted   4 0 0   100%
tests/test_helpers.py TestNormalizeAmounts.test_already_numeric_left_alone   3 0 0   100%
tests/test_helpers.py TestNormalizeAmounts.test_empty_values_untouched   4 0 0   100%
tests/test_helpers.py TestDfDateFilter.dates_df   1 0 0   100%
tests/test_helpers.py TestDfDateFilter.test_filters_inclusive_range   3 0 0   100%
tests/test_helpers.py TestDfDateFilter.test_no_bounds_returns_unchanged   2 0 0   100%
tests/test_helpers.py TestDfDateFilter.test_only_start_bound   2 0 0   100%
tests/test_helpers.py TestDfDateFilter.test_only_end_bound   2 0 0   100%
tests/test_helpers.py TestDfDateFilter.test_empty_result_raises   2 0 0   100%
tests/test_helpers.py (no function)   45 0 0   100%
tests/test_row_labels.py TestInit.test_init   4 0 0   100%
tests/test_row_labels.py TestInit.test_missing_required_column_raises   2 0 0   100%
tests/test_row_labels.py TestInit.test_duplicate_category_name_raises   3 0 0   100%
tests/test_row_labels.py TestInit.test_empty_category_name_is_skipped   3 0 0   100%
tests/test_row_labels.py TestInit.test_deductions_source_skips_dag_edge   4 0 0   100%
tests/test_row_labels.py TestInit.test_s_tag_adds_node_not_edge   4 0 0   100%
tests/test_row_labels.py TestInit.test_tag_type_not_added_to_dag   3 0 0   100%
tests/test_row_labels.py TestPaths.test_get_longest_path   3 0 0   100%
tests/test_row_labels.py TestPaths.test_get_path   3 0 0   100%
tests/test_row_labels.py TestPaths.test_get_path_no_match_returns_none   1 0 0   100%
tests/test_row_labels.py TestLabelLookup.test_get_label   4 0 0   100%
tests/test_row_labels.py TestLabelLookup.test_get_label_unknown_returns_none   1 0 0   100%
tests/test_row_labels.py TestLabelLookup.test_get_attribute   3 0 0   100%
tests/test_row_labels.py TestLabelLookup.test_get_attribute_unknown_attribute_raises   2 0 0   100%
tests/test_row_labels.py TestLabelLookup.test_get_attribute_missing_label_uses_default   4 0 0   100%
tests/test_row_labels.py TestLabelLookup.test_get_attribute_missing_label_no_default_returns_none   1 0 0   100%
tests/test_row_labels.py TestPrintGraph.test_print_graph_writes_file   3 0 0   100%
tests/test_row_labels.py (no function)   24 0 0   100%
tests/test_transactions.py TestInit.test_init   3 0 0   100%
tests/test_transactions.py TestInit.test_validate_df   1 0 0   100%
tests/test_transactions.py TestInit.test_init_raises_on_non_float_amount   3 0 0   100%
tests/test_transactions.py TestAudit.test_audit_no_missing_transactions   2 0 0   100%
tests/test_transactions.py TestAudit.test_audit_reports_missing_transaction   3 0 0   100%
tests/test_transactions.py TestFilterTags.test_filter_tags_drops_matching_rows   3 0 0   100%
tests/test_transactions.py TestFilterTags.test_filter_tags_no_match_is_noop   2 0 0   100%
tests/test_transactions.py TestApplyLabelsBasic.test_apply_labels_resolves_default_source_target   3 0 0   100%
tests/test_transactions.py tag_labels_data   1 0 0   100%
tests/test_transactions.py tag_row_labels   1 0 0   100%
tests/test_transactions.py _transactions_for   4 0 0   100%
tests/test_transactions.py TestApplyLabelsTagBranches.test_tag_append_mode   4 0 0   100%
tests/test_transactions.py TestApplyLabelsTagBranches.test_tag_override_matched   4 0 0   100%
tests/test_transactions.py TestApplyLabelsTagBranches.test_tag_override_unmatched_falls_back_to_income   4 0 0   100%
tests/test_transactions.py TestApplyLabelsTagBranches.test_s_tag_redirects_income_source   4 0 0   100%
tests/test_transactions.py TestApplyLabelsTagBranches.test_store_match   4 0 0   100%
tests/test_transactions.py TestApplyLabelsTagBranches.test_recurring_redirects_income_source   5 0 0   100%
tests/test_transactions.py TestApplyLabelsTagBranches.test_deductions_source_uses_description   5 0 0   100%
tests/test_transactions.py TestApplyLabelsTagBranches.test_orphan_edge_raises   5 0 0   100%
tests/test_transactions.py TestProcessRows.test_sales_tax_creates_synthetic_row_and_updates_amount   8 0 0   100%
tests/test_transactions.py TestProcessRows.test_separate_taxes_routes_from_income   7 0 0   100%
tests/test_transactions.py TestProcessRows.test_tips_creates_synthetic_row_and_updates_amount   7 0 0   100%
tests/test_transactions.py TestProcessRows.test_multi_hop_dag_creates_synthetic_intermediate_row   8 0 0   100%
tests/test_transactions.py TestCollapse.test_collapse_aggregates_shared_pairs   5 0 0   100%
tests/test_transactions.py TestSurplusDeficit.test_income_surplus   7 0 0   100%
tests/test_transactions.py TestSurplusDeficit.test_income_deficit   7 0 0   100%
tests/test_transactions.py TestSurplusDeficit.test_s_tag_surplus_kept_separate_by_default   7 0 0   100%
tests/test_transactions.py TestSurplusDeficit.test_s_tag_surplus_feeds_back_to_income   6 0 0   100%
tests/test_transactions.py TestFilterDates.dated_transactions   1 0 0   100%
tests/test_transactions.py TestFilterDates.test_filters_to_inclusive_range   4 0 0   100%
tests/test_transactions.py TestFilterDates.test_none_none_is_noop   2 0 0   100%
tests/test_transactions.py TestFilterDates.test_open_ended_start_uses_earliest   2 0 0   100%
tests/test_transactions.py TestFilterDates.test_start_after_end_raises   2 0 0   100%
tests/test_transactions.py TestFilterDates.test_empty_result_raises   2 0 0   100%
tests/test_transactions.py TestDistributeAmounts.test_forward_distribution_splits_amount_and_dates   8 0 0   100%
tests/test_transactions.py TestDistributeAmounts.test_reverse_distribution_moves_dates_backward   5 0 0   100%
tests/test_transactions.py TestDistributeAmounts.test_sales_tax_distributed_alongside_amount   3 0 0   100%
tests/test_transactions.py TestDistributeAmounts.test_calling_twice_is_noop   5 0 0   100%
tests/test_transactions.py TestDistributeAmounts.test_distribution_before_apply_labels_does_not_crash   8 0 0   100%
tests/test_transactions.py TestDistributeAmounts.test_no_distribution_rows_untouched   3 0 0   100%
tests/test_transactions.py TestFullProcessPipeline.test_process_marks_surplus_deficit_processed   3 0 0   100%
tests/test_transactions.py (no function)   58 0 0   100%
Total     1962 245 0   88%

No items found using the specified filter.