warn-scraper 1.2.168.dev0__tar.gz → 1.2.169.dev0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/.github/workflows/continuous-deployment.yml +7 -7
  2. {warn_scraper-1.2.168.dev0/warn_scraper.egg-info → warn_scraper-1.2.169.dev0}/PKG-INFO +1 -1
  3. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/sources.md +3 -3
  4. warn_scraper-1.2.169.dev0/warn/scrapers/nd.py +96 -0
  5. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0/warn_scraper.egg-info}/PKG-INFO +1 -1
  6. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn_scraper.egg-info/SOURCES.txt +1 -0
  7. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn_scraper.egg-info/scm_file_list.json +1 -0
  8. warn_scraper-1.2.169.dev0/warn_scraper.egg-info/scm_version.json +8 -0
  9. warn_scraper-1.2.168.dev0/warn_scraper.egg-info/scm_version.json +0 -8
  10. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/.devcontainer/devcontainer.json +0 -0
  11. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/.github/dependabot-disabled.yml +0 -0
  12. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/.github/workflows/continuous-deployment.yml.broken-tests +0 -0
  13. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/.gitignore +0 -0
  14. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/.pre-commit-config.yaml +0 -0
  15. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/LICENSE +0 -0
  16. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/MANIFEST.in +0 -0
  17. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/Makefile +0 -0
  18. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/Pipfile +0 -0
  19. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/Pipfile.lock +0 -0
  20. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/README.md +0 -0
  21. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/Makefile +0 -0
  22. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/R42693.pdf +0 -0
  23. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/gao-03-1003.pdf +0 -0
  24. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/releasing-actions-finished.png +0 -0
  25. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/releasing-actions-start.png +0 -0
  26. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/releasing-changelog-button.png +0 -0
  27. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/releasing-changelog-entered.png +0 -0
  28. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/releasing-draft-button.png +0 -0
  29. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/releasing-name-release.png +0 -0
  30. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/releasing-name-tag.png +0 -0
  31. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/releasing-publish-button.png +0 -0
  32. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/releasing-pypi.png +0 -0
  33. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/releasing-release-published.png +0 -0
  34. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/releasing-releases-button.png +0 -0
  35. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_static/releasing-tag-button.png +0 -0
  36. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/_templates/sources.md.tmpl +0 -0
  37. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/conf.py +0 -0
  38. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/contributing.rst +0 -0
  39. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/index.rst +0 -0
  40. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/make.bat +0 -0
  41. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/reference.rst +0 -0
  42. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/releasing.md +0 -0
  43. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/requirements.txt +0 -0
  44. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/al.md +0 -0
  45. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/az.md +0 -0
  46. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/ca.md +0 -0
  47. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/co.md +0 -0
  48. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/dc.md +0 -0
  49. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/de.md +0 -0
  50. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/ia.md +0 -0
  51. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/in.md +0 -0
  52. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/job_center.md +0 -0
  53. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/ks.md +0 -0
  54. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/md.md +0 -0
  55. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/me.md +0 -0
  56. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/mo.md +0 -0
  57. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/ny.md +0 -0
  58. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/ok.md +0 -0
  59. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/or.md +0 -0
  60. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/sc.md +0 -0
  61. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/tx.md +0 -0
  62. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/ut.md +0 -0
  63. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/va.md +0 -0
  64. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/vt.md +0 -0
  65. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/scrapers/wi.md +0 -0
  66. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/docs/usage.md +0 -0
  67. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/pyproject.toml +0 -0
  68. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/setup.cfg +0 -0
  69. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/setup.py +0 -0
  70. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/__init__.py +0 -0
  71. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/cassettes/test_cached_detail_pages.yaml +0 -0
  72. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/cassettes/test_cached_search_results.yaml +0 -0
  73. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/cassettes/test_missing_detail_page_values.yaml +0 -0
  74. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/cassettes/test_no_results.yaml +0 -0
  75. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/cassettes/test_paged_results.yaml +0 -0
  76. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/cassettes/test_scrape_integration.yaml +0 -0
  77. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/conftest.py +0 -0
  78. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/fixtures/2021_page_1.html +0 -0
  79. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/fixtures/2021_page_2.html +0 -0
  80. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/test_cache.py +0 -0
  81. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/test_delete.py +0 -0
  82. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/test_job_center.py +0 -0
  83. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/test_job_center_cache.py +0 -0
  84. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/tests/test_openpyxl.py +0 -0
  85. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/__init__.py +0 -0
  86. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/cache.py +0 -0
  87. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/cli.py +0 -0
  88. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/pdfrodent/__init__.py +0 -0
  89. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/pdfrodent/pdfrodent.py +0 -0
  90. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/platforms/__init__.py +0 -0
  91. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/platforms/job_center/__init__.py +0 -0
  92. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/platforms/job_center/cache.py +0 -0
  93. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/platforms/job_center/site.py +0 -0
  94. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/platforms/job_center/urls.py +0 -0
  95. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/platforms/job_center/utils.py +0 -0
  96. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/runner.py +0 -0
  97. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/__init__.py +0 -0
  98. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/ak.py +0 -0
  99. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/al.py +0 -0
  100. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/az.py +0 -0
  101. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/ca.py +0 -0
  102. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/co.py +0 -0
  103. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/ct.py +0 -0
  104. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/dc.py +0 -0
  105. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/de.py +0 -0
  106. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/fl.py +0 -0
  107. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/ga.py +0 -0
  108. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/hi.py +0 -0
  109. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/ia.py +0 -0
  110. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/id.py +0 -0
  111. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/il.py +0 -0
  112. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/in.py +0 -0
  113. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/ks.py +0 -0
  114. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/ky.py +0 -0
  115. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/la.py +0 -0
  116. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/md.py +0 -0
  117. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/me.py +0 -0
  118. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/mi.py +0 -0
  119. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/mo.py +0 -0
  120. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/ms.py +0 -0
  121. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/mt.py +0 -0
  122. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/ne.py +0 -0
  123. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/nj.py +0 -0
  124. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/nm.py +0 -0
  125. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/ny.py +0 -0
  126. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/oh.py +0 -0
  127. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/ok.py +0 -0
  128. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/or.py +0 -0
  129. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/pa.py +0 -0
  130. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/ri.py +0 -0
  131. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/sc.py +0 -0
  132. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/sd.py +0 -0
  133. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/tn.py +0 -0
  134. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/tx.py +0 -0
  135. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/ut.py +0 -0
  136. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/va.py +0 -0
  137. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/vt.py +0 -0
  138. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/wa.py +0 -0
  139. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/scrapers/wi.py +0 -0
  140. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn/utils.py +0 -0
  141. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn_scraper.egg-info/dependency_links.txt +0 -0
  142. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn_scraper.egg-info/entry_points.txt +0 -0
  143. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn_scraper.egg-info/requires.txt +0 -0
  144. {warn_scraper-1.2.168.dev0 → warn_scraper-1.2.169.dev0}/warn_scraper.egg-info/top_level.txt +0 -0
@@ -11,7 +11,7 @@ jobs:
11
11
  steps:
12
12
  - id: checkout
13
13
  name: Checkout
14
- uses: actions/checkout@v6
14
+ uses: actions/checkout@v7
15
15
 
16
16
  - id: setup-python
17
17
  name: Setup Python
@@ -21,7 +21,7 @@ jobs:
21
21
 
22
22
  - id: pre-commit
23
23
  name: Pre-commit
24
- uses: pre-commit/action@v3.0.1
24
+ uses: biglocalnews/pre-commit-action@v4.0.0
25
25
 
26
26
  test-docs:
27
27
  name: Test Sphinx build
@@ -29,7 +29,7 @@ jobs:
29
29
  steps:
30
30
  - id: checkout
31
31
  name: Checkout
32
- uses: actions/checkout@v6
32
+ uses: actions/checkout@v7
33
33
 
34
34
  - id: setup-python
35
35
  name: Setup Python
@@ -58,7 +58,7 @@ jobs:
58
58
 
59
59
  - id: save
60
60
  name: Save artifact
61
- uses: actions/upload-artifact@v6
61
+ uses: actions/upload-artifact@v7
62
62
  with:
63
63
  name: docs-${{ github.run_number }}
64
64
  path: ./docs
@@ -108,7 +108,7 @@ jobs:
108
108
  steps:
109
109
  - id: checkout
110
110
  name: Checkout
111
- uses: actions/checkout@v6
111
+ uses: actions/checkout@v7
112
112
 
113
113
  - id: setup-python
114
114
  name: Setup Python
@@ -157,7 +157,7 @@ jobs:
157
157
 
158
158
  - id: fetch
159
159
  name: Fetch artifact
160
- uses: actions/download-artifact@v7
160
+ uses: actions/download-artifact@v8
161
161
  with:
162
162
  name: test-release-${{ github.run_number }}
163
163
  path: ./dist
@@ -169,4 +169,4 @@ jobs:
169
169
  user: __token__
170
170
  password: ${{ secrets.PYPI_API_TOKEN }}
171
171
  verbose: true
172
- verify_metadata: false
172
+ verify-metadata: false
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: warn-scraper
3
- Version: 1.2.168.dev0
3
+ Version: 1.2.169.dev0
4
4
  Summary: Command-line interface for downloading WARN Act notices of qualified plant closings and mass layoffs from state government websites
5
5
  Author-email: Big Local News <biglocalnews@stanford.edu>
6
6
  License-Expression: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  # Sources
2
2
 
3
- There are currently scrapers for 39 of America's 56 states and territories.
3
+ There are currently scrapers for 40 of America's 56 states and territories.
4
4
 
5
5
  | State | Source | Docs | Authors | Tags |
6
6
  | :---- | :----: | :--: | :------ | :--- |
@@ -29,6 +29,7 @@ There are currently scrapers for 39 of America's 56 states and territories.
29
29
  |[New Jersey](https://github.com/biglocalnews/warn-scraper/blob/main/warn/scrapers/nj.py)|[🔗](https://www.nj.gov/labor/employer-services/warn/)||[Dilcia19](https://github.com/Dilcia19), [palewire](https://github.com/palewire), [zstumgoren](https://github.com/zstumgoren)|html|
30
30
  |[New Mexico](https://github.com/biglocalnews/warn-scraper/blob/main/warn/scrapers/nm.py)|[🔗](https://www.dws.state.nm.us/Rapid-Response)||[chriszs](https://github.com/chriszs)|pdf|
31
31
  |[New York](https://github.com/biglocalnews/warn-scraper/blob/main/warn/scrapers/ny.py)|[🔗](https://dol.ny.gov/warn-notices)|[📃](scrapers/ny.md)|[Dilcia19](https://github.com/Dilcia19), [palewire](https://github.com/palewire), [ydoc5212](https://github.com/ydoc5212), [zstumgoren](https://github.com/zstumgoren)|excel, historical|
32
+ |[North Dakota](https://github.com/biglocalnews/warn-scraper/blob/main/warn/scrapers/nd.py)|[🔗](https://www.jobsnd.com/documents)||[riordan](https://github.com/riordan)|pdf|
32
33
  |[Ohio](https://github.com/biglocalnews/warn-scraper/blob/main/warn/scrapers/oh.py)|[🔗](https://jfs.ohio.gov/warn/index.stm)||[Dilcia19](https://github.com/Dilcia19), [chriszs](https://github.com/chriszs), [zstumgoren](https://github.com/zstumgoren)|html, pdf|
33
34
  |[Oklahoma](https://github.com/biglocalnews/warn-scraper/blob/main/warn/scrapers/ok.py)|[🔗](https://okjobmatch.com/search/warn_lookups/new)|[📃](scrapers/ok.md)|[Dilcia19](https://github.com/Dilcia19), [zstumgoren](https://github.com/zstumgoren)|jobcenter|
34
35
  |[Oregon](https://github.com/biglocalnews/warn-scraper/blob/main/warn/scrapers/or.py)|[🔗](https://ccwd.hecc.oregon.gov/Layoff/WARN)|[📃](scrapers/or.md)|[Dilcia19](https://github.com/Dilcia19), [ydoc5212](https://github.com/ydoc5212), [zstumgoren](https://github.com/zstumgoren)|excel, historical|
@@ -47,7 +48,7 @@ There are currently scrapers for 39 of America's 56 states and territories.
47
48
 
48
49
  ## To do
49
50
 
50
- These 17 areas need a scraper:
51
+ These 16 areas need a scraper:
51
52
 
52
53
  - Arkansas
53
54
  - Hawaii
@@ -57,7 +58,6 @@ These 17 areas need a scraper:
57
58
  - Nevada
58
59
  - New Hampshire
59
60
  - North Carolina
60
- - North Dakota
61
61
  - Pennsylvania
62
62
  - West Virginia
63
63
  - Wyoming
@@ -0,0 +1,96 @@
1
+ import logging
2
+ import re
3
+ from pathlib import Path
4
+
5
+ import pdfplumber
6
+
7
+ from .. import utils
8
+ from ..cache import Cache
9
+
10
+ __authors__ = ["riordan"]
11
+ __tags__ = ["pdf"]
12
+ __source__ = {
13
+ "name": "Job Service North Dakota",
14
+ "url": "https://www.jobsnd.com/documents",
15
+ }
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ def scrape(
21
+ data_dir: Path = utils.WARN_DATA_DIR,
22
+ cache_dir: Path = utils.WARN_CACHE_DIR,
23
+ ) -> Path:
24
+ """
25
+ Scrape data from North Dakota.
26
+
27
+ Keyword arguments:
28
+ data_dir -- the Path were the result will be saved (default WARN_DATA_DIR)
29
+ cache_dir -- the Path where results can be cached (default WARN_CACHE_DIR)
30
+
31
+ Returns: the Path where the file is written
32
+ """
33
+ # Fire up the cache
34
+ cache = Cache(cache_dir)
35
+ state_code = "nd"
36
+
37
+ # North Dakota publishes a single static PDF covering 2015 to the present.
38
+ pdf_url = "https://www.jobsnd.com/sites/www/files/documents/jsnd-documents/WARN%20Notices%202015%20to%20present.pdf"
39
+
40
+ # Download the PDF and stash the raw file, unedited, in the cache
41
+ cache_key = f"{state_code}/WARN_Notices_2015_to_present.pdf"
42
+ pdf_path = cache.download(cache_key, pdf_url)
43
+
44
+ # Loop through the PDF pages and pull out the table
45
+ output_rows: list = []
46
+ header_written = False
47
+ with pdfplumber.open(pdf_path) as pdf:
48
+ for page_index, page in enumerate(pdf.pages):
49
+ rows = page.extract_table() or []
50
+
51
+ for row in rows:
52
+ # Standardize each cell
53
+ output_row = [_clean_text(cell) for cell in row]
54
+
55
+ # Skip fully empty rows
56
+ if not any(output_row):
57
+ continue
58
+
59
+ # The header repeats at the top of the table. Keep it once.
60
+ if output_row[0] == "Company Name":
61
+ if header_written:
62
+ logger.debug(
63
+ f"Skipping repeated header row on page {page_index + 1}"
64
+ )
65
+ continue
66
+ header_written = True
67
+
68
+ output_rows.append(output_row)
69
+
70
+ # Write out to CSV
71
+ data_path = data_dir / f"{state_code}.csv"
72
+ utils.write_rows_to_csv(data_path, output_rows)
73
+
74
+ # Return the path
75
+ return data_path
76
+
77
+
78
+ def _clean_text(text: str) -> str:
79
+ """
80
+ Clean up text from a PDF cell.
81
+
82
+ Keyword arguments:
83
+ text -- the text to clean
84
+
85
+ Returns: the cleaned text
86
+ """
87
+ # Replace None with an empty string
88
+ if text is None:
89
+ return ""
90
+
91
+ # Collapse newlines and standardize whitespace
92
+ return re.sub(r"\s+", " ", text).strip()
93
+
94
+
95
+ if __name__ == "__main__":
96
+ scrape()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: warn-scraper
3
- Version: 1.2.168.dev0
3
+ Version: 1.2.169.dev0
4
4
  Summary: Command-line interface for downloading WARN Act notices of qualified plant closings and mass layoffs from state government websites
5
5
  Author-email: Big Local News <biglocalnews@stanford.edu>
6
6
  License-Expression: Apache-2.0
@@ -113,6 +113,7 @@ warn/scrapers/mi.py
113
113
  warn/scrapers/mo.py
114
114
  warn/scrapers/ms.py
115
115
  warn/scrapers/mt.py
116
+ warn/scrapers/nd.py
116
117
  warn/scrapers/ne.py
117
118
  warn/scrapers/nj.py
118
119
  warn/scrapers/nm.py
@@ -77,6 +77,7 @@
77
77
  "warn/scrapers/al.py",
78
78
  "warn/scrapers/fl.py",
79
79
  "warn/scrapers/nj.py",
80
+ "warn/scrapers/nd.py",
80
81
  "warn/scrapers/mi.py",
81
82
  "warn/scrapers/vt.py",
82
83
  "warn/scrapers/mt.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "1.2.168",
3
+ "distance": 0,
4
+ "node": "gf7b3dd26af1f3ad700762504bd7c5c0d23979507",
5
+ "dirty": true,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-07-14"
8
+ }
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "1.2.167",
3
- "distance": 0,
4
- "node": "g37743b43bf883960669ce90f639f848b54050751",
5
- "dirty": true,
6
- "branch": "HEAD",
7
- "node_date": "2026-07-13"
8
- }