From 62d11abb9d83387fba122f726275dc04d141f16e Mon Sep 17 00:00:00 2001 From: Greg Pomerantz Date: Mon, 31 Aug 2026 21:29:29 -0400 Subject: [PATCH] Keep existing event CSVs when a download carries no events of that kind Yahoo stopped returning capitalGain events for some funds (2026); a blind os.Create + write of an empty event set overwrote the only copy of that history. When the new download has zero rows of an event kind and the existing CSV has data rows, keep the existing file. (gmp/f data.py also falls back to overrides/event-backup/ as a second line of defense.) --- main.go | 78 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/main.go b/main.go index 8881201..e211cb2 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "sort" + "strings" "time" ) @@ -219,47 +220,52 @@ func Conv(name string) { adjclose[k], vol)) } if dividends != nil { - out, err = os.Create(name + "-dividend.csv") - if err != nil { - fmt.Printf("%s: %s\n", name, err) - os.Exit(-1) - } - - defer out.Close() - - out.WriteString("Date,Dividends\n") - for _, r := range eventRows(name, "dividend", dividends, loc) { - out.WriteString(fmt.Sprintf("%s,%f\n", r.date, r.amount)) - } + writeEvents(name, name+"-dividend.csv", "Date,Dividends", + eventRows(name, "dividend", dividends, loc), + func(r eventRow) string { return fmt.Sprintf("%s,%f", r.date, r.amount) }) } if capgains != nil { - out, err = os.Create(name + "-capitalGain.csv") - if err != nil { - fmt.Printf("%s: %s\n", name, err) - os.Exit(-1) - } - - defer out.Close() - - out.WriteString("Date,Capital Gains\n") - for _, r := range eventRows(name, "capital gain", capgains, loc) { - out.WriteString(fmt.Sprintf("%s,%f\n", r.date, r.amount)) - } + writeEvents(name, name+"-capitalGain.csv", "Date,Capital Gains", + eventRows(name, "capital gain", capgains, loc), + func(r eventRow) string { return fmt.Sprintf("%s,%f", r.date, r.amount) }) } if splits != nil { - out, err = os.Create(name + "-split.csv") - if err != nil { - fmt.Printf("%s: %s\n", name, err) - os.Exit(-1) - } - - defer out.Close() - - out.WriteString("Date,Stock Splits\n") - for _, r := range eventRows(name, "split", splits, loc) { - out.WriteString(fmt.Sprintf("%s,%s\n", r.date, r.ratio)) - } + writeEvents(name, name+"-split.csv", "Date,Stock Splits", + eventRows(name, "split", splits, loc), + func(r eventRow) string { return fmt.Sprintf("%s,%s", r.date, r.ratio) }) + } +} + +// fileHasDataRows reports whether path exists and has a row beyond the header. +func fileHasDataRows(path string) bool { + b, err := os.ReadFile(path) + if err != nil { + return false + } + lines := strings.Split(strings.TrimSpace(string(b)), "\n") + return len(lines) > 1 && strings.TrimSpace(lines[1]) != "" +} + +// writeEvents writes the event rows to path, unless the download carries no +// rows of this kind while an existing populated file is present: Yahoo has +// stopped returning capitalGain events for some funds (2026), and a blind +// overwrite would wipe the only copy of that history. (The consumer side, +// gmp/f data.py, additionally falls back to a backup directory.) +func writeEvents(name, path, header string, rows []eventRow, format func(eventRow) string) { + if len(rows) == 0 && fileHasDataRows(path) { + fmt.Printf("%s: no %s events in download; keeping existing %s\n", name, header, path) + return + } + out, err := os.Create(path) + if err != nil { + fmt.Printf("%s: %s\n", name, err) + os.Exit(-1) + } + defer out.Close() + out.WriteString(header + "\n") + for _, r := range rows { + out.WriteString(format(r) + "\n") } }