ltc-code 0.2.11__tar.gz → 0.2.12__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 (59) hide show
  1. {ltc_code-0.2.11 → ltc_code-0.2.12}/PKG-INFO +1 -1
  2. {ltc_code-0.2.11 → ltc_code-0.2.12}/pyproject.toml +1 -1
  3. ltc_code-0.2.12/src/ltc_code/nsc/__init__.py +0 -0
  4. ltc_code-0.2.12/src/ltc_code/nsc/naics.csv +25 -0
  5. ltc_code-0.2.12/src/ltc_code/nsc/naics.py +126 -0
  6. ltc_code-0.2.12/src/ltc_code/nsc/naics_raw.xlsx +0 -0
  7. {ltc_code-0.2.11 → ltc_code-0.2.12}/README.md +0 -0
  8. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/.DS_Store +0 -0
  9. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/aspire.py +0 -0
  10. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/check_cmo_apps.do +0 -0
  11. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/christel_house.py +0 -0
  12. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/democracy_prep.py +0 -0
  13. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/green_dot.py +0 -0
  14. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/helpers.py +0 -0
  15. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/ilt.py +0 -0
  16. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/kipp_nj.py +0 -0
  17. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/main.py +0 -0
  18. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/mappings.py +0 -0
  19. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/rocketship.py +0 -0
  20. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/yes_prep.py +0 -0
  21. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/aspire.py +0 -0
  22. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/christel_house.py +0 -0
  23. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/democracy_prep.py +0 -0
  24. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/green_dot.py +0 -0
  25. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/ilt.py +0 -0
  26. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/kipp.py +0 -0
  27. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/kipp_nj.py +0 -0
  28. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/rocketship.py +0 -0
  29. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/yes_prep.py +0 -0
  30. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/BALANCE.do +0 -0
  31. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/FS.do +0 -0
  32. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/ITT.do +0 -0
  33. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/TOT.do +0 -0
  34. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/apps_helpers.py +0 -0
  35. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/harmony.py +0 -0
  36. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/main.py +0 -0
  37. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260712_uncommon_scripts/main.py +0 -0
  38. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260712_uncommon_scripts/mappings.py +0 -0
  39. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/20260712_uncommon_scripts/uncommon.py +0 -0
  40. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/__init__.py +0 -0
  41. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/aspire.py +0 -0
  42. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/check_cmo_apps.do +0 -0
  43. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/christel_house.py +0 -0
  44. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/green_dot.py +0 -0
  45. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/helpers.py +0 -0
  46. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/june13.py +0 -0
  47. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/june2.py +0 -0
  48. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/june30.py +0 -0
  49. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/june5.py +0 -0
  50. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/june7.py +0 -0
  51. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/kipp_nj.py +0 -0
  52. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/kipp_tx.py +0 -0
  53. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/main.py +0 -0
  54. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/mappings.py +0 -0
  55. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/may27.py +0 -0
  56. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/polars_dates.py +0 -0
  57. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/rocketship.py +0 -0
  58. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/schema_mapping.py +0 -0
  59. {ltc_code-0.2.11 → ltc_code-0.2.12}/src/ltc_code/yes_prep.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ltc-code
3
- Version: 0.2.11
3
+ Version: 0.2.12
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.9
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ltc-code"
3
- version = "0.2.11"
3
+ version = "0.2.12"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
File without changes
@@ -0,0 +1,25 @@
1
+ naics_code,sector
2
+ 11,"Agriculture, Forestry, Fishing and Hunting"
3
+ 21,"Mining, Quarrying, and Oil and Gas Extraction"
4
+ 22,Utilities
5
+ 23,Construction
6
+ 31,Manufacturing
7
+ 32,Manufacturing
8
+ 33,Manufacturing
9
+ 42,Wholesale Trade
10
+ 44,Retail Trade
11
+ 45,Retail Trade
12
+ 48,Transportation and Warehousing
13
+ 49,Transportation and Warehousing
14
+ 51,Information
15
+ 52,Finance and Insurance
16
+ 53,Real Estate and Rental and Leasing
17
+ 54,"Professional, Scientific, and Technical Services"
18
+ 55,Management of Companies and Enterprises
19
+ 56,Administrative and Support and Waste Management and Remediation Services
20
+ 61,Educational Services
21
+ 62,Health Care and Social Assistance
22
+ 71,"Arts, Entertainment, and Recreation"
23
+ 72,Accommodation and Food Services
24
+ 81,Other Services (except Public Administration)
25
+ 92,Public Administration
@@ -0,0 +1,126 @@
1
+ from __future__ import annotations
2
+
3
+ import csv
4
+ import re
5
+ import zipfile
6
+ from pathlib import Path
7
+ from xml.etree import ElementTree as ET
8
+
9
+
10
+ BASE_DIR = Path(__file__).resolve().parent
11
+ RAW_DIR = BASE_DIR
12
+ CLEAN_DIR = BASE_DIR
13
+ OUTPUT_PATH = BASE_DIR / "naics.csv"
14
+
15
+ NS = {"x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"}
16
+ SECTOR_CODE_RE = re.compile(r"^\s*(\d{2})(?:\s*-\s*(\d{2}))?\s*$")
17
+ LETTER_RE = re.compile(r"[A-Za-z]")
18
+
19
+
20
+ def _shared_strings(workbook: zipfile.ZipFile) -> list[str]:
21
+ try:
22
+ root = ET.fromstring(workbook.read("xl/sharedStrings.xml"))
23
+ except KeyError:
24
+ return []
25
+
26
+ strings: list[str] = []
27
+ for item in root.findall("x:si", NS):
28
+ strings.append("".join(text.text or "" for text in item.findall(".//x:t", NS)))
29
+ return strings
30
+
31
+
32
+ def _cell_value(cell: ET.Element, shared_strings: list[str]) -> str:
33
+ cell_type = cell.attrib.get("t")
34
+
35
+ if cell_type == "inlineStr":
36
+ return "".join(text.text or "" for text in cell.findall(".//x:t", NS)).strip()
37
+
38
+ value = cell.find("x:v", NS)
39
+ if value is None or value.text is None:
40
+ return ""
41
+
42
+ text = value.text.strip()
43
+ if cell_type == "s":
44
+ return shared_strings[int(text)].strip()
45
+ return text
46
+
47
+
48
+ def read_first_sheet_rows(path: Path) -> list[list[str]]:
49
+ with zipfile.ZipFile(path) as workbook:
50
+ shared_strings = _shared_strings(workbook)
51
+ sheet = ET.fromstring(workbook.read("xl/worksheets/sheet1.xml"))
52
+
53
+ rows: list[list[str]] = []
54
+ for row in sheet.findall(".//x:sheetData/x:row", NS):
55
+ values = [
56
+ _cell_value(cell, shared_strings)
57
+ for cell in row.findall("x:c", NS)
58
+ ]
59
+ rows.append(values)
60
+ return rows
61
+
62
+
63
+ def expand_sector_code(code: str) -> list[str]:
64
+ match = SECTOR_CODE_RE.fullmatch(code)
65
+ if match is None:
66
+ return []
67
+
68
+ start = int(match.group(1))
69
+ end = int(match.group(2) or match.group(1))
70
+ return [f"{sector:02d}" for sector in range(start, end + 1)]
71
+
72
+
73
+ def clean_naics_rows(rows: list[list[str]]) -> list[dict[str, str]]:
74
+ cleaned: list[dict[str, str]] = []
75
+ seen: set[tuple[str, str]] = set()
76
+
77
+ for row in rows:
78
+ values = [value.strip() for value in row if value.strip()]
79
+ if len(values) < 2:
80
+ continue
81
+
82
+ for index, value in enumerate(values[:-1]):
83
+ sector_codes = expand_sector_code(value)
84
+ if not sector_codes:
85
+ continue
86
+
87
+ sector_name = values[index + 1].strip()
88
+ if (
89
+ not sector_name
90
+ or not LETTER_RE.search(sector_name)
91
+ or sector_name.lower() in {"sector", "title", "description"}
92
+ ):
93
+ continue
94
+
95
+ for sector_code in sector_codes:
96
+ record = (sector_code, sector_name)
97
+ if record in seen:
98
+ continue
99
+ cleaned.append({"naics_code": sector_code, "sector": sector_name})
100
+ seen.add(record)
101
+ break
102
+
103
+ return cleaned
104
+
105
+
106
+ def main() -> None:
107
+ source_files = sorted(RAW_DIR.glob("*.xlsx"))
108
+ if not source_files:
109
+ raise FileNotFoundError(f"No .xlsx files found in {RAW_DIR}")
110
+
111
+ rows = read_first_sheet_rows(source_files[0])
112
+ cleaned = clean_naics_rows(rows)
113
+ if not cleaned:
114
+ raise ValueError(f"No sector-level NAICS rows found in {source_files[0]}")
115
+
116
+ CLEAN_DIR.mkdir(parents=True, exist_ok=True)
117
+ with OUTPUT_PATH.open("w", newline="", encoding="utf-8") as output_file:
118
+ writer = csv.DictWriter(output_file, fieldnames=["naics_code", "sector"])
119
+ writer.writeheader()
120
+ writer.writerows(cleaned)
121
+
122
+ print(f"Wrote {len(cleaned)} rows to {OUTPUT_PATH}")
123
+
124
+
125
+ if __name__ == "__main__":
126
+ main()
File without changes