fix: handle duplicate security names and non-JSON error responses
Build Service / build (push) Canceled after 0s
Build Service / build (push) Canceled after 0s
- Disambiguate duplicate (company, security) pairs in Carta exports: e.g. two 'Warrants' under BIP21 become 'Warrants' and 'Warrants (2)' - Tested against Fund II: 33 holdings, 41 positions (3 companies with duplicate tranches), zero errors - Frontend: graceful handling of 500 responses (shows status code instead of JSON parse crash)
This commit is contained in:
@@ -269,6 +269,8 @@ def _parse_schedule_xlsx(file_bytes: bytes) -> tuple[str | None, list[dict], lis
|
|||||||
# Parse data rows (starting at row 6)
|
# Parse data rows (starting at row 6)
|
||||||
current_company: str | None = None
|
current_company: str | None = None
|
||||||
seen_companies: set[str] = set()
|
seen_companies: set[str] = set()
|
||||||
|
# Track (company, security) occurrences to disambiguate duplicate tranches
|
||||||
|
security_counts: dict[tuple[str, str], int] = {}
|
||||||
|
|
||||||
for row_idx in range(6, ws.max_row + 1):
|
for row_idx in range(6, ws.max_row + 1):
|
||||||
col_a = ws.cell(row=row_idx, column=1).value # Investment (company)
|
col_a = ws.cell(row=row_idx, column=1).value # Investment (company)
|
||||||
@@ -301,9 +303,18 @@ def _parse_schedule_xlsx(file_bytes: bytes) -> tuple[str | None, list[dict], lis
|
|||||||
|
|
||||||
# Position row: col B has security name
|
# Position row: col B has security name
|
||||||
if col_b:
|
if col_b:
|
||||||
security_name = str(col_b).strip()
|
raw_security_name = str(col_b).strip()
|
||||||
company = current_company or "(unknown)"
|
company = current_company or "(unknown)"
|
||||||
|
|
||||||
|
# Disambiguate duplicate (company, security) pairs
|
||||||
|
# e.g. two "Warrants" under BIP21 become "Warrants" and "Warrants (2)"
|
||||||
|
pair_key = (company, raw_security_name)
|
||||||
|
security_counts[pair_key] = security_counts.get(pair_key, 0) + 1
|
||||||
|
if security_counts[pair_key] == 1:
|
||||||
|
security_name = raw_security_name
|
||||||
|
else:
|
||||||
|
security_name = f"{raw_security_name} ({security_counts[pair_key]})"
|
||||||
|
|
||||||
# Parse investment date
|
# Parse investment date
|
||||||
inv_date: date | None = None
|
inv_date: date | None = None
|
||||||
if isinstance(col_c, datetime):
|
if isinstance(col_c, datetime):
|
||||||
|
|||||||
@@ -66,7 +66,14 @@ export default function Import() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const res = await fetch(url, { method: "POST", body: form });
|
const res = await fetch(url, { method: "POST", body: form });
|
||||||
const data = await res.json();
|
let data: any;
|
||||||
|
try {
|
||||||
|
data = await res.json();
|
||||||
|
} catch {
|
||||||
|
setError(`Server error (${res.status}). Check file format and try again.`);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
setError(data.detail || "Import failed");
|
setError(data.detail || "Import failed");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user