Guilherme Silberfarb Costa commited on
Commit
a20fa71
·
1 Parent(s): 02cb858

Harden portable shapefile diagnostics

Browse files
backend/app/core/shapefile_runtime.py CHANGED
@@ -35,8 +35,44 @@ def _iter_features_pyshp(shapefile_path: Path, *, target_crs: str | None) -> lis
35
  from shapely.geometry import shape
36
  from shapely.ops import transform as shapely_transform
37
 
38
- reader = shapefile.Reader(str(shapefile_path))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  field_names = [str(field[0]) for field in reader.fields[1:]]
41
  source_crs = None
42
  prj_path = shapefile_path.with_suffix(".prj")
@@ -48,25 +84,45 @@ def _iter_features_pyshp(shapefile_path: Path, *, target_crs: str | None) -> lis
48
  transformer = _build_transformer(source_crs, target_crs)
49
 
50
  features: list[tuple[dict[str, Any], Any]] = []
51
- for shape_record in reader.iterShapeRecords():
52
- values = list(shape_record.record)
53
- properties = {
54
- field_name: values[index] if index < len(values) else None
55
- for index, field_name in enumerate(field_names)
56
- }
57
- geometry_raw = getattr(shape_record.shape, "__geo_interface__", None)
58
- geometry = None
59
- if geometry_raw and geometry_raw.get("coordinates"):
60
- geometry = shape(geometry_raw)
61
- if transformer is not None:
62
- geometry = shapely_transform(transformer.transform, geometry)
63
- features.append((properties, geometry))
 
 
 
 
 
 
 
 
 
 
64
  return features
 
 
 
 
65
  finally:
66
  try:
67
- reader.close()
 
68
  except Exception:
69
  pass
 
 
 
 
 
70
 
71
 
72
  def _iter_features_fiona(shapefile_path: Path, *, target_crs: str | None) -> list[tuple[dict[str, Any], Any]]:
 
35
  from shapely.geometry import shape
36
  from shapely.ops import transform as shapely_transform
37
 
38
+ shp_path = shapefile_path.with_suffix(".shp")
39
+ shx_path = shapefile_path.with_suffix(".shx")
40
+ dbf_path = shapefile_path.with_suffix(".dbf")
41
+
42
+ def _describe_sidecars() -> str:
43
+ parts: list[str] = []
44
+ for candidate in (shp_path, shx_path, dbf_path, shapefile_path.with_suffix(".prj"), shapefile_path.with_suffix(".cpg")):
45
+ if candidate.exists():
46
+ try:
47
+ parts.append(f"{candidate.name}={candidate.stat().st_size}")
48
+ except Exception:
49
+ parts.append(f"{candidate.name}=present")
50
+ else:
51
+ parts.append(f"{candidate.name}=missing")
52
+ return ", ".join(parts)
53
+
54
+ if not shp_path.exists():
55
+ raise FileNotFoundError(f"Shapefile nao encontrado: {shp_path}")
56
+
57
+ handles = []
58
+ reader = None
59
  try:
60
+ shp_handle = open(shp_path, "rb")
61
+ handles.append(shp_handle)
62
+ shx_handle = open(shx_path, "rb") if shx_path.exists() else None
63
+ if shx_handle is not None:
64
+ handles.append(shx_handle)
65
+ dbf_handle = open(dbf_path, "rb") if dbf_path.exists() else None
66
+ if dbf_handle is not None:
67
+ handles.append(dbf_handle)
68
+
69
+ reader = shapefile.Reader(
70
+ shp=shp_handle,
71
+ shx=shx_handle,
72
+ dbf=dbf_handle,
73
+ encoding="utf-8",
74
+ encodingErrors="replace",
75
+ )
76
  field_names = [str(field[0]) for field in reader.fields[1:]]
77
  source_crs = None
78
  prj_path = shapefile_path.with_suffix(".prj")
 
84
  transformer = _build_transformer(source_crs, target_crs)
85
 
86
  features: list[tuple[dict[str, Any], Any]] = []
87
+ record_errors: list[str] = []
88
+ for index, shape_record in enumerate(reader.iterShapeRecords()):
89
+ try:
90
+ values = list(shape_record.record)
91
+ properties = {
92
+ field_name: values[field_index] if field_index < len(values) else None
93
+ for field_index, field_name in enumerate(field_names)
94
+ }
95
+ geometry_raw = getattr(shape_record.shape, "__geo_interface__", None)
96
+ geometry = None
97
+ if geometry_raw and geometry_raw.get("coordinates"):
98
+ geometry = shape(geometry_raw)
99
+ if transformer is not None:
100
+ geometry = shapely_transform(transformer.transform, geometry)
101
+ features.append((properties, geometry))
102
+ except Exception as exc:
103
+ if len(record_errors) < 5:
104
+ record_errors.append(f"registro {index}: {exc!r}")
105
+ continue
106
+
107
+ if not features and record_errors:
108
+ detalhe = "; ".join(record_errors)
109
+ raise RuntimeError(f"nenhum registro legivel via pyshp ({detalhe})")
110
  return features
111
+ except Exception as exc:
112
+ raise RuntimeError(
113
+ f"pyshp falhou para {shapefile_path.name}: {exc!r}; arquivos: {_describe_sidecars()}"
114
+ ) from exc
115
  finally:
116
  try:
117
+ if reader is not None:
118
+ reader.close()
119
  except Exception:
120
  pass
121
+ for handle in handles:
122
+ try:
123
+ handle.close()
124
+ except Exception:
125
+ pass
126
 
127
 
128
  def _iter_features_fiona(shapefile_path: Path, *, target_crs: str | None) -> list[tuple[dict[str, Any], Any]]:
build/windows/smoke_test_portable.ps1 CHANGED
@@ -61,6 +61,16 @@ function Show-StartupDiagnostics {
61
  Write-Host "--- MesaFrame-startup.log ---"
62
  Get-Content -Path $startupLogPath
63
  }
 
 
 
 
 
 
 
 
 
 
64
  }
65
 
66
  $proc = Start-Process -FilePath $exePath -WorkingDirectory $appDirResolved -PassThru
 
61
  Write-Host "--- MesaFrame-startup.log ---"
62
  Get-Content -Path $startupLogPath
63
  }
64
+ $dadosDir = Join-Path $appDirResolved "_internal/app/core/dados"
65
+ if (Test-Path $dadosDir) {
66
+ Write-Host "--- dados bundle ---"
67
+ Get-ChildItem -Path $dadosDir | Select-Object Name, Length | Format-Table -AutoSize | Out-String | Write-Host
68
+ }
69
+ $logoPath = Join-Path $appDirResolved "_internal/frontend/dist/logo_mesa.png"
70
+ if (Test-Path $logoPath) {
71
+ Write-Host "--- logo bundle ---"
72
+ Get-Item $logoPath | Select-Object FullName, Length | Format-Table -AutoSize | Out-String | Write-Host
73
+ }
74
  }
75
 
76
  $proc = Start-Process -FilePath $exePath -WorkingDirectory $appDirResolved -PassThru