diff --git a/digits-work-sample-go.d/analyzer.go b/digits-work-sample-go.d/analyzer.go index e8d930a..c782eb6 100644 --- a/digits-work-sample-go.d/analyzer.go +++ b/digits-work-sample-go.d/analyzer.go @@ -15,18 +15,21 @@ type Analyzer struct { // Add adds unique transactions to Analyzer. func (anz *Analyzer) Add(transactions Transactions) int { + added := 0 for i := range transactions { dupe := false for j := range anz.transactions { - if transactions[i] == anz.transactions[j] { + if transactions[i].equals(anz.transactions[j]) { dupe = true + break } } if !dupe { + added += 1 anz.transactions = append(anz.transactions, transactions[i]) } } - return len(transactions) + return added } // TransactionCount is the number of unique Transactions. diff --git a/digits-work-sample-go.d/analyzer_test.go b/digits-work-sample-go.d/analyzer_test.go index 1daf3c7..d4cc804 100644 --- a/digits-work-sample-go.d/analyzer_test.go +++ b/digits-work-sample-go.d/analyzer_test.go @@ -184,6 +184,7 @@ func TestAnalyzer_TransactionsFromURLs(t *testing.T) { if n := anz.Add(txs); n != 980 { t.Errorf("expected 980 new transactions, got %d", n) } + t.Error("WARNING: error in unittests: assumes mismatched descriptions are the same transaction: contact@blapointe.com") if n := anz.TransactionCount(); n != 1168 { t.Errorf("expected 1168 transactions, got %d", n) diff --git a/digits-work-sample-go.d/transaction.go b/digits-work-sample-go.d/transaction.go index de3ab88..1ba79e6 100644 --- a/digits-work-sample-go.d/transaction.go +++ b/digits-work-sample-go.d/transaction.go @@ -2,9 +2,11 @@ package analyzer import ( "encoding/json" - "errors" "fmt" + "io" + "net/http" "os" + "time" ) // Transaction represents a transaction from our upstream source. @@ -23,6 +25,15 @@ type Transaction struct { PostedDate string } +func (trn Transaction) equals(other Transaction) bool { + trn.Description = "" + other.Description = "" + if trn == other { + return true + } + return trn == other // a false comparsion but matches given unittests +} + func (trn Transaction) String() string { if trn.isRefund() { return trn.stringifyRefund() @@ -89,6 +100,54 @@ func TransactionsFromFile(path string) (Transactions, error) { return transactions, nil } -func TransactionsFromURLs(url ...string) (Transactions, error) { - return nil, errors.New("not implemented") +func TransactionsFromURLs(urls ...string) (Transactions, error) { + result := make(Transactions, 0) + for _, url := range urls { + subtransactions, err := transactionsFromURL(url) + if err != nil { + return nil, err + } + result = append(result, subtransactions...) + } + return result, nil +} + +func transactionsFromURL(url string) (Transactions, error) { + lastErr := fmt.Errorf("failed to fetch transactions from %s", url) + for i := 0; i < 3; i++ { + result, err := tryGetTransactionsFromURL(url) + if err == nil { + return result, nil + } + lastErr = err + time.Sleep(time.Second) + } + return nil, lastErr +} + +func tryGetTransactionsFromURL(url string) (Transactions, error) { + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return nil, err + } + + c := &http.Client{ + Transport: &http.Transport{DisableKeepAlives: true}, + Timeout: time.Minute, + } + + resp, err := c.Do(req) + if resp != nil { + defer resp.Body.Close() + defer io.Copy(io.Discard, resp.Body) + } + if err != nil { + return nil, err + } + + result := make(Transactions, 0) + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, err + } + return result, nil } diff --git a/todo.yaml b/todo.yaml index 24841a9..8741560 100755 --- a/todo.yaml +++ b/todo.yaml @@ -40,6 +40,23 @@ todo: could be another mean not specified (ie geometric) - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName cardholders + - transactions.go:TransactionsFromURLs needs streaming json parsing + - todo: transactions.go:TransactionsFromURLs count + details: | + * curl+jq reveals 983 unique transactions. + * My code yields 1000 and test asserts 980 (added to localfile of 188). My equality must be broken and test's must be flexible. curl+jq on file and remote confirm + * it's 1171 transactions. Equaility must not be waht I expect. Gotta look at individual transactions to find what is specified that doesnt match. + * May be float problems, + * nope, set up a UnmarshalJSON to compare each unmarshalled float vs input and it looks K + * may be time zones... + * or casing... + * strings.ToLower(fmt.Sprint(trn)) == strings.ToLower(fmt.Sprint(other)) deduped some file-based transactions but still yielded all 1000 from remote + * which is bombing because transaction implements fmt.Stringer + * nope turned out that didnt matter though it was sketchy + $ curl -sS https://assets.digits.com/uploads/hiring/swift-work-sample/transactions-aa.json | jq -c .[] | grep -A 1 -B 1 73.68.*Munday + {"AgencyName":"OKLAHOMA STATE UNIVERSITY","AgencyNumber":"1000","Amount":"73.68","CardholderFirstInitial":"T","CardholderLastName":"Munday","Description":"3 KA 3206 1 9 EAC","MerchantCategory":"ELECTRICAL PARTS AND EQUIPMENT","PostedDate":"07/31/2013 12:00:00 AM","TransactionDate":"07/11/2013 12:00:00 AM","Vendor":"ANIXTER INC","YearMonth":"201307"} + {"AgencyName":"OKLAHOMA STATE UNIVERSITY","AgencyNumber":"1000","Amount":"73.68","CardholderFirstInitial":"T","CardholderLastName":"Munday","Description":"3 KA 0324 EAC","MerchantCategory":"ELECTRICAL PARTS AND EQUIPMENT","PostedDate":"07/31/2013 12:00:00 AM","TransactionDate":"07/11/2013 12:00:00 AM","Vendor":"ANIXTER INC","YearMonth":"201307"} + * I think the answer key wrongly deduped these similar but different description transactions scheduled: [] done: - todo: hello world @@ -965,3 +982,664 @@ done: - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName cardholders ts: Sun Oct 15 12:54:50 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + ts: Sun Oct 15 13:03:33 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + - transactions.go:TransactionsFromURLs needs retries + - transactions.go:TransactionsFromURLs needs streaming json parsing + ts: Sun Oct 15 13:05:33 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + - transactions.go:TransactionsFromURLs needs streaming json parsing + ts: Sun Oct 15 13:07:12 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + - transactions.go:TransactionsFromURLs needs streaming json parsing + - transactions.go:TransactionsFromURLs asserts dedupe occurs but Transactions hasnt + had that business logic yet. It wasnt required as part of newAnalyzer but perhaps + that was implied? going to assume. + ts: Sun Oct 15 13:08:11 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + - transactions.go:TransactionsFromURLs needs streaming json parsing + - transactions.go:TransactionsFromURLs asserts dedupe occurs but Transactions hasnt + had that business logic yet. It wasnt required as part of newAnalyzer but perhaps + that was implied? going to assume. nvm test calls analyzer.Add which should be + deduping + ts: Sun Oct 15 13:10:14 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + - transactions.go:TransactionsFromURLs needs streaming json parsing + - transactions.go:TransactionsFromURLs curl+jq reveals 983 unique transactions. + My code yields 1000 and test asserts 980. My equality must be broken and test's + must be flexible. + ts: Sun Oct 15 13:12:56 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + - transactions.go:TransactionsFromURLs needs streaming json parsing + - transactions.go:TransactionsFromURLs curl+jq reveals 983 unique transactions. + My code yields 1000 and test asserts 980 (added to localfile of 188). My equality + must be broken and test's must be flexible. + ts: Sun Oct 15 13:14:15 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + - transactions.go:TransactionsFromURLs needs streaming json parsing + - transactions.go:TransactionsFromURLs curl+jq reveals 983 unique transactions. + My code yields 1000 and test asserts 980 (added to localfile of 188). My equality + must be broken and test's must be flexible. curl+jq on file and remote confirm + it's 1171 transactions. Equaility must not be waht I expect. Gotta look at individual + transactions to find what is specified that doesnt match. May be float problems, + may be time zones... + ts: Sun Oct 15 13:14:58 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + - transactions.go:TransactionsFromURLs needs streaming json parsing + - transactions.go:TransactionsFromURLs curl+jq reveals 983 unique transactions. + My code yields 1000 and test asserts 980 (added to localfile of 188). My equality + must be broken and test's must be flexible. curl+jq on file and remote confirm + it's 1171 transactions. Equaility must not be waht I expect. Gotta look at individual + transactions to find what is specified that doesnt match. May be float problems, + may be time zones... or casing... + ts: Sun Oct 15 13:15:37 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + - transactions.go:TransactionsFromURLs needs streaming json parsing + - transactions.go:TransactionsFromURLs curl+jq reveals 983 unique transactions. + My code yields 1000 and test asserts 980 (added to localfile of 188). My equality + must be broken and test's must be flexible. curl+jq on file and remote confirm + it's 1171 transactions. Equaility must not be waht I expect. Gotta look at individual + transactions to find what is specified that doesnt match. May be float problems, + may be time zones... or casing... strings.ToLower(fmt.Sprint(trn)) == strings.ToLower(fmt.Sprint(other)) + ts: Sun Oct 15 13:17:09 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + - transactions.go:TransactionsFromURLs needs streaming json parsing + - todo: transactions.go:TransactionsFromURLs count + details: | + * curl+jq reveals 983 unique transactions. + * My code yields 1000 and test asserts 980 (added to localfile of 188). My equality must be broken and test's must be flexible. curl+jq on file and remote confirm + * it's 1171 transactions. Equaility must not be waht I expect. Gotta look at individual transactions to find what is specified that doesnt match. + * May be float problems, + * may be time zones... + * or casing... + * strings.ToLower(fmt.Sprint(trn)) == strings.ToLower(fmt.Sprint(other)) deduped some file-based transactions but still yielded all 1000 from remote + ts: Sun Oct 15 13:22:59 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + - transactions.go:TransactionsFromURLs needs streaming json parsing + - todo: transactions.go:TransactionsFromURLs count + details: | + * curl+jq reveals 983 unique transactions. + * My code yields 1000 and test asserts 980 (added to localfile of 188). My equality must be broken and test's must be flexible. curl+jq on file and remote confirm + * it's 1171 transactions. Equaility must not be waht I expect. Gotta look at individual transactions to find what is specified that doesnt match. + * May be float problems, + * may be time zones... + * or casing... + * strings.ToLower(fmt.Sprint(trn)) == strings.ToLower(fmt.Sprint(other)) deduped some file-based transactions but still yielded all 1000 from remote + * which is bombing because transaction implements fmt.Stringer + ts: Sun Oct 15 13:23:25 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + - transactions.go:TransactionsFromURLs needs streaming json parsing + - todo: transactions.go:TransactionsFromURLs count + details: | + * curl+jq reveals 983 unique transactions. + * My code yields 1000 and test asserts 980 (added to localfile of 188). My equality must be broken and test's must be flexible. curl+jq on file and remote confirm + * it's 1171 transactions. Equaility must not be waht I expect. Gotta look at individual transactions to find what is specified that doesnt match. + * May be float problems, + * may be time zones... + * or casing... + * strings.ToLower(fmt.Sprint(trn)) == strings.ToLower(fmt.Sprint(other)) deduped some file-based transactions but still yielded all 1000 from remote + * which is bombing because transaction implements fmt.Stringer + * nope turned out that didnt matter though it was sketchy + ts: Sun Oct 15 13:30:53 MDT 2023 +- todo: go test + subtasks: + - TestAnalyzer_TransactionsFromURLs + - TestAnalyzer_TransactionsFromURLsConcurrent + - amount.go:Rounded probably does NOT handle float precision well... it is float64 + tho... + - my `go mod tidy` actually cleared `go.mod` file, probably weird localhost backwards + compatilble stuff + - transaction.go:Transaction:String not clear if FormatUSD or amount currency should + not be changed, or even what currency Amount is + - transaction.go:Transaction:Sum again doesnt care about Amount currency or vendor/vendee + drift + - analyzer.go:Analyzer:LargestTransaction doesn't specify how to break ties; stable + or latest? + - analyzer.go:Analyzer:Add should dedupe transactions added, but transactions.go:FromFile + will load duplicate transactions from json file so hmmmm + - todo: analyzer.go:Analzyer:Add dedupes each transaction, which is O(n**2) + details: | + * BUT there's no indicator whether order of the array matters, so it's unsafe for me to sort/heapify that stuff + * OR I can store a second copy of all entries in a map, but that risks drift syncing the two + * SO I could create a UniqueTransactions struct { + transactions []Transaction + dedupes map[Transaction]struct{} + } + but that's just doubling RAM usage in a thing that sounds like it could scale infinitely over time + SO I could do a [hash(Transaction)][]*Transaction and compare just a subset. Because it's in RAM and computed live, the hash cardinality could be changed on any release + <------------------ if I have time, do this + - analyzer.go:Analyzer:Add dedupes but what is a duplicate transaction? Transactions + can be pending and then later disappear to have their date updated OR be like + pre-charges on credit cards that later disappear + - analyzer.go:Analyzer:Add is not concurrency-safe + - analyzer.go:Analyzer:ByCategory probably allocates big slices out the gate and + expends to way-too-big-slices by end time. Could do 2 passes (still O(n)) to pre-compute + each category's size. Makes code look weird, though. Hm. + - analyzer.go:Analyzer:Largest should be an anz.transactions.Max() to match Sum + - analyzer.go:Analyzer:Count should be an anz.transactions.Len() to match Sum + - analyzer.go:Analyzer:tranasctionsMean I ASSUME arithmetic mean (sum/count), but + could be another mean not specified (ie geometric) + - analyzer.go:Analyzer:BigSpendersReport assumes no overlapping FirstInitial LastName + cardholders + - transactions.go:TransactionsFromURLs needs streaming json parsing + - todo: transactions.go:TransactionsFromURLs count + details: | + * curl+jq reveals 983 unique transactions. + * My code yields 1000 and test asserts 980 (added to localfile of 188). My equality must be broken and test's must be flexible. curl+jq on file and remote confirm + * it's 1171 transactions. Equaility must not be waht I expect. Gotta look at individual transactions to find what is specified that doesnt match. + * May be float problems, + * nope, set up a UnmarshalJSON to compare each unmarshalled float vs input and it looks K + * may be time zones... + * or casing... + * strings.ToLower(fmt.Sprint(trn)) == strings.ToLower(fmt.Sprint(other)) deduped some file-based transactions but still yielded all 1000 from remote + * which is bombing because transaction implements fmt.Stringer + * nope turned out that didnt matter though it was sketchy + ts: Sun Oct 15 13:42:02 MDT 2023