Skip to content

Commit 0403dd0

Browse files
committed
Address review: count ranges, empty-candidate guard, CSV newlines, dead code
1 parent 4e2dcf5 commit 0403dd0

7 files changed

Lines changed: 24 additions & 13 deletions

File tree

breach_scraper/cli.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,6 @@ def scrape_all_sources(
230230
retries: int = 3,
231231
) -> list[dict[str, Any]]:
232232
combined: list[dict[str, Any]] = []
233-
failures: list[str] = []
234233
for source in _selected_sources(source_keys):
235234
try:
236235
LOGGER.info("Fetching source %s", source.key)
@@ -254,14 +253,9 @@ def scrape_all_sources(
254253
)
255254
combined.extend(_annotate_record(record, source) for record in filtered_records)
256255
except Exception as exc:
257-
message = f"source {source.key} failed: {exc}"
258-
failures.append(message)
259-
LOGGER.error(message)
260256
if strict:
261-
raise SourceRunError(message) from exc
262-
263-
if failures and not combined and strict:
264-
raise SourceRunError("; ".join(failures))
257+
raise SourceRunError(f"source {source.key} failed: {exc}") from exc
258+
LOGGER.error("source %s failed: %s", source.key, exc)
265259

266260
return _sort_records(combined)
267261

breach_scraper/http.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ def fetch_url(
4141
(capped at 30s). HTTP 403 raises an actionable error; other 4xx are not
4242
retried and fall through to the next candidate.
4343
"""
44+
if not candidates:
45+
raise ValueError("At least one candidate URL must be provided.")
46+
4447
request_headers = {"User-Agent": user_agent or DEFAULT_USER_AGENT, **DEFAULT_HEADERS}
4548
if headers:
4649
request_headers.update(headers)

breach_scraper/output.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def _to_csv(records: list[dict[str, Any]]) -> str:
102102
if key not in columns:
103103
columns.append(key)
104104
buffer = StringIO()
105-
writer = csv.DictWriter(buffer, fieldnames=columns)
105+
writer = csv.DictWriter(buffer, fieldnames=columns, lineterminator="\n")
106106
writer.writeheader()
107107
writer.writerows(records)
108108
return buffer.getvalue()

breach_scraper/sources/wa_atg.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ def _normalize_count(value: str) -> str:
4848
cleaned = _clean_text(value)
4949
if not cleaned:
5050
return ""
51-
digits = re.sub(r"[^\d]", "", cleaned)
52-
if not digits:
53-
return cleaned
54-
return f"{int(digits):,}"
51+
no_commas = cleaned.replace(",", "")
52+
if no_commas.isdigit():
53+
return f"{int(no_commas):,}"
54+
return cleaned
5555

5656

5757
def _field_sort_key(item: tuple[str, str]) -> tuple[int, int | str]:

tests/test_cli.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ def test_default_window_filters_old_records(self) -> None:
6060
self.assertEqual(rc, 0)
6161
self.assertEqual(json.loads(out), [])
6262

63+
def test_offline_csv_has_unix_line_endings(self) -> None:
64+
rc, out, _ = run(["--input-html", str(FIXTURE), "--output", "csv", *RANGE])
65+
self.assertEqual(rc, 0)
66+
self.assertNotIn("\r", out)
67+
self.assertIn("date_reported,organization_name", out)
68+
6369

6470
class TestCliOnline(unittest.TestCase):
6571
@mock.patch("breach_scraper.http.urlopen")

tests/test_http.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ def test_candidate_fallback(self, mock_urlopen: mock.Mock, _sleep: mock.Mock) ->
8787
self.assertEqual(result, "ok")
8888
self.assertEqual(mock_urlopen.call_count, 2)
8989

90+
def test_empty_candidates_raises_value_error(self) -> None:
91+
with self.assertRaises(ValueError):
92+
fetch_url([])
93+
9094

9195
if __name__ == "__main__":
9296
unittest.main()

tests/test_wa_atg.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ def test_normalize_record_dates_and_counts(self) -> None:
2828
self.assertEqual(out["date_reported"], "2024-06-01")
2929
self.assertEqual(out["number_of_washingtonians_affected"], "1,234")
3030

31+
def test_normalize_count_keeps_ranges_intact(self) -> None:
32+
out = normalize_record({"number_of_washingtonians_affected": "1,200 - 1,500"})
33+
self.assertEqual(out["number_of_washingtonians_affected"], "1,200 - 1,500")
34+
3135

3236
if __name__ == "__main__":
3337
unittest.main()

0 commit comments

Comments
 (0)