saulsolis09 commited on
Commit
4fbbaed
·
1 Parent(s): 1548940

Added Delimiter-Sniffer Logic

Browse files
Files changed (1) hide show
  1. app.py +50 -6
app.py CHANGED
@@ -56,7 +56,7 @@ FILTER_DOMINANT_COUNTY = False # process ALL counties (multi-county / full-stat
56
  # "csv" -> Master_Dataset.zip, every result bundled as a CSV (faster: no Excel
57
  # writing -- use this for very large / state-sized runs)
58
  # The individual per-category files are CSV in both modes.
59
- MASTER_OUTPUT = "csv"
60
 
61
  # How records are processed:
62
  # "stream" -> ONE COUNTY AT A TIME. The input is split into per-county temp
@@ -117,10 +117,49 @@ def get_safety_list_path() -> str:
117
  _SAFETY_CACHE: dict | None = None
118
 
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  def build_whitelist_from_path(path):
121
  """Parse a safety-list file (xlsx or csv) into {pairs, cities, rows, fips_name}."""
122
  if str(path).lower().endswith(".csv"):
123
- safety_df = pd.read_csv(path)
124
  else:
125
  safety_df = pd.read_excel(path)
126
  acceptable_cols = [c for c in safety_df.columns if str(c).startswith("Acceptable FIPS")]
@@ -661,10 +700,13 @@ def run_pipeline(uploaded_files, reference_file, blank_cities_not_in_list,
661
  name = f.name
662
  try:
663
  if str(name).lower().endswith(".csv"):
664
- dfs.append(pd.read_csv(name, low_memory=False))
 
 
 
665
  else:
666
  dfs.append(pd.read_excel(name))
667
- _step(log, f"Loaded {Path(name).name} ({len(dfs[-1]):,} rows)")
668
  except Exception as e:
669
  _step(log, f"ERROR loading {Path(name).name}: {e}")
670
  if not dfs:
@@ -1235,7 +1277,7 @@ def _split_by_county(uploaded_files, out_dir, log, chunksize=STREAM_CHUNK_ROWS):
1235
  # detect the FIPS-county column from a small sample of the first file
1236
  first = uploaded_files[0].name
1237
  if str(first).lower().endswith(".csv"):
1238
- sample = pd.read_csv(first, nrows=50_000, low_memory=False)
1239
  else:
1240
  sample = pd.read_excel(first)
1241
  fips_col, reason = detect_fips_county_column(sample)
@@ -1263,7 +1305,9 @@ def _split_by_county(uploaded_files, out_dir, log, chunksize=STREAM_CHUNK_ROWS):
1263
  for f in uploaded_files:
1264
  nm = f.name
1265
  if str(nm).lower().endswith(".csv"):
1266
- for chunk in pd.read_csv(nm, chunksize=chunksize, low_memory=False):
 
 
1267
  route(chunk)
1268
  total += len(chunk)
1269
  _step(log, f"Split: routed {total:,} rows into {len(county_paths)} counties")
 
56
  # "csv" -> Master_Dataset.zip, every result bundled as a CSV (faster: no Excel
57
  # writing -- use this for very large / state-sized runs)
58
  # The individual per-category files are CSV in both modes.
59
+ MASTER_OUTPUT = "excel"
60
 
61
  # How records are processed:
62
  # "stream" -> ONE COUNTY AT A TIME. The input is split into per-county temp
 
117
  _SAFETY_CACHE: dict | None = None
118
 
119
 
120
+ def detect_delimiter(path, candidates=(",", "|", "^", "\t", ";")):
121
+ """Sniff a text file's field delimiter from its first lines. SQL exports here
122
+ are sometimes '^' or '|' rather than ','. Returns the delimiter char (default
123
+ ','). Detects once from a small header sample so the actual (possibly chunked)
124
+ read can still use pandas' fast C parser with an explicit sep."""
125
+ try:
126
+ with open(path, "r", encoding="utf-8", errors="replace", newline="") as fh:
127
+ lines = []
128
+ for _ in range(8):
129
+ ln = fh.readline()
130
+ if not ln:
131
+ break
132
+ ln = ln.rstrip("\r\n")
133
+ if ln:
134
+ lines.append(ln)
135
+ except Exception:
136
+ return ","
137
+ if not lines:
138
+ return ","
139
+ best, best_score = ",", -1
140
+ for d in candidates:
141
+ header_count = lines[0].count(d)
142
+ if header_count == 0:
143
+ continue
144
+ # reward a high, consistent field count across the sampled rows
145
+ consistent = sum(1 for ln in lines if ln.count(d) == header_count)
146
+ score = header_count * 100 + consistent
147
+ if score > best_score:
148
+ best, best_score = d, score
149
+ return best
150
+
151
+
152
+ def _read_csv_auto(path, **kwargs):
153
+ """pd.read_csv with the delimiter auto-detected (unless caller passed sep)."""
154
+ if "sep" not in kwargs:
155
+ kwargs["sep"] = detect_delimiter(path)
156
+ return pd.read_csv(path, **kwargs)
157
+
158
+
159
  def build_whitelist_from_path(path):
160
  """Parse a safety-list file (xlsx or csv) into {pairs, cities, rows, fips_name}."""
161
  if str(path).lower().endswith(".csv"):
162
+ safety_df = _read_csv_auto(path)
163
  else:
164
  safety_df = pd.read_excel(path)
165
  acceptable_cols = [c for c in safety_df.columns if str(c).startswith("Acceptable FIPS")]
 
700
  name = f.name
701
  try:
702
  if str(name).lower().endswith(".csv"):
703
+ sep = detect_delimiter(name)
704
+ dfs.append(pd.read_csv(name, low_memory=False, sep=sep))
705
+ _step(log, f"Loaded {Path(name).name} ({len(dfs[-1]):,} rows, "
706
+ f"delimiter {sep!r})")
707
  else:
708
  dfs.append(pd.read_excel(name))
709
+ _step(log, f"Loaded {Path(name).name} ({len(dfs[-1]):,} rows)")
710
  except Exception as e:
711
  _step(log, f"ERROR loading {Path(name).name}: {e}")
712
  if not dfs:
 
1277
  # detect the FIPS-county column from a small sample of the first file
1278
  first = uploaded_files[0].name
1279
  if str(first).lower().endswith(".csv"):
1280
+ sample = pd.read_csv(first, nrows=50_000, low_memory=False, sep=detect_delimiter(first))
1281
  else:
1282
  sample = pd.read_excel(first)
1283
  fips_col, reason = detect_fips_county_column(sample)
 
1305
  for f in uploaded_files:
1306
  nm = f.name
1307
  if str(nm).lower().endswith(".csv"):
1308
+ sep = detect_delimiter(nm)
1309
+ _step(log, f"Reading {Path(nm).name} (delimiter {sep!r})")
1310
+ for chunk in pd.read_csv(nm, chunksize=chunksize, low_memory=False, sep=sep):
1311
  route(chunk)
1312
  total += len(chunk)
1313
  _step(log, f"Split: routed {total:,} rows into {len(county_paths)} counties")