Coverage for src/fjc_biographies/main.py: 100%
28 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-22 22:47 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-22 22:47 +0000
1"""Downloads, parses, and exports the FJC's judge biographies in one step.
3`run` saves the tidy table of each of the FJC's files (see `parser.tidy`)
4and the roster in two formats, by appointment (see `parser.build_roster`)
5and by judge (see `parser.roster_by_judge`), as CSV files in the data
6folder:
8 data_folder/
9 ├── raw/ the FJC's files, as they were downloaded
10 ├── demographics.csv the tidy table of each file, by its
11 ├── service.csv category
12 ├── ...
13 ├── roster_by_appointment.csv a row for each court that each judge
14 │ served on
15 ├── roster_by_judge.csv a row for each judge, with each of the
16 │ judge's appointments numbered
17 └── martin_quinn_terms.csv with `martin_quinn`, each justice's
18 Martin-Quinn score in each term
20With `jcs` or `martin_quinn`, the roster has Judicial Common Space or
21Martin-Quinn scores (see `scores`).
23Contents:
24 export: saves a table as a CSV file.
25 run: downloads, parses, and exports the FJC's files.
27"""
29from __future__ import annotations
31import logging
32import pathlib
34import pandas as pd
36from . import downloader, parser, rules, scores
38logger = logging.getLogger(__name__)
41def run(
42 folder: pathlib.Path | str | None = None,
43 *,
44 update: bool = False,
45 rulebook: rules.Rulebook | pathlib.Path | str = 'judges',
46 jcs: bool = False,
47 martin_quinn: bool | pathlib.Path | str = False,
48 scores_folder: pathlib.Path | str | None = None) -> dict[
49 str, pathlib.Path]:
50 """Downloads, parses, and exports the FJC's files.
52 Args:
53 folder: the data folder. Defaults to None, in which case
54 `downloader.data_folder()` is used.
55 update: whether to download the files again even if they are there,
56 to get the judges and corrections since they were downloaded.
57 Defaults to False.
58 rulebook: the rules that make the roster's fields about judges'
59 careers (see `parser.code_careers`). Defaults to "judges".
60 jcs: whether to add each appointment's Judicial Common Space score
61 (see `scores.add_jcs`). Defaults to False.
62 martin_quinn: whether to add the justices' Martin-Quinn scores (see
63 `scores.add_martin_quinn`) and export their scores in each term,
64 or the path of the file of the scores ("justices.csv"), which is
65 also used for the justices' Judicial Common Space scores.
66 Defaults to False.
67 scores_folder: the folder for the files that the scores come from.
68 Defaults to None, in which case the data folder of
69 judicial-common-space is used.
71 Returns:
72 The path of each CSV file that was saved, by its category, the
73 paths of the roster as "roster_by_appointment" and
74 "roster_by_judge", and with `martin_quinn`, the path of the
75 justices' scores in each term as "martin_quinn_terms".
77 Examples:
78 >>> import fjc_biographies
79 >>> fjc_biographies.run(update = True) # doctest: +SKIP
81 """
82 root = pathlib.Path(folder).expanduser() if folder else (
83 downloader.data_folder())
84 tables = parser.load_all(root, update = update)
85 roster = parser.build_roster(
86 tables['service'], tables['demographics'], tables['career'],
87 rulebook = rulebook)
88 # A path names the file of Martin-Quinn scores, and True finds it.
89 path = None if isinstance(martin_quinn, bool) else martin_quinn
90 if jcs:
91 roster = scores.add_jcs(
92 roster, root, scores_folder = scores_folder, martin_quinn = path,
93 update = update)
94 terms = None
95 if martin_quinn:
96 terms = scores.martin_quinn_terms(
97 root, scores_folder = scores_folder, martin_quinn = path,
98 update = update and not jcs)
99 roster = scores.add_martin_quinn(roster, terms = terms)
100 tables['roster_by_appointment'] = roster
101 tables['roster_by_judge'] = parser.roster_by_judge(roster)
102 if terms is not None:
103 tables['martin_quinn_terms'] = terms
104 return {
105 name: export(table, root / f'{name}.csv')
106 for name, table in tables.items()}
109def export(table: pd.DataFrame, path: pathlib.Path | str) -> pathlib.Path:
110 """Saves a table as a CSV file.
112 Dates are written as "YYYY-MM-DD", and missing values as blank cells.
114 Args:
115 table: the table.
116 path: where to save it. Its folder is created if needed.
118 Returns:
119 The path the table was saved to.
121 """
122 saved = pathlib.Path(path).expanduser()
123 saved.parent.mkdir(parents = True, exist_ok = True)
124 table.to_csv(
125 saved, index = False, encoding = 'utf-8', date_format = '%Y-%m-%d')
126 logger.info('Saved %s rows to %s.', len(table), saved.name)
127 return saved