From 5d9879657b9d6763e7d325d722764cd0d8676208 Mon Sep 17 00:00:00 2001 From: abhishek Date: Mon, 27 Jul 2026 11:42:32 +0530 Subject: [PATCH] auto lat and long --- scratch/backfill_location_coordinates.go | 146 +++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 scratch/backfill_location_coordinates.go diff --git a/scratch/backfill_location_coordinates.go b/scratch/backfill_location_coordinates.go new file mode 100644 index 0000000..9337ddb --- /dev/null +++ b/scratch/backfill_location_coordinates.go @@ -0,0 +1,146 @@ +//go:build ignore + +package main + +import ( + "database/sql" + "encoding/json" + "fmt" + "log" + "net/http" + "net/url" + "strings" + "time" + + _ "github.com/jackc/pgx/v5/stdlib" +) + +// One-off backfill for tenants/tenantlocations rows that were onboarded +// before the admin console geocoded addresses automatically, so their +// latitude/longitude columns were left blank. Geocodes each row's existing +// address via OpenStreetMap Nominatim (same keyless provider +// AddressAutocomplete.tsx already uses on the frontend) and writes the +// result back. Run manually: `go run scratch/backfill_location_coordinates.go` +// — do not wire this into any request path, it's a single pass over +// historical rows. +const nominatimURL = "https://nominatim.openstreetmap.org/search" + +// Nominatim's usage policy caps free lookups at ~1 request/sec. +const rateLimit = 1100 * time.Millisecond + +type nominatimRow struct { + Lat string `json:"lat"` + Lon string `json:"lon"` +} + +func geocode(client *http.Client, address string) (lat, lon string, ok bool) { + q := url.Values{} + q.Set("format", "json") + q.Set("limit", "1") + q.Set("q", address) + + req, err := http.NewRequest("GET", nominatimURL+"?"+q.Encode(), nil) + if err != nil { + return "", "", false + } + // Required by Nominatim's usage policy — identifies the calling app. + req.Header.Set("User-Agent", "fiesta-backend-backfill/1.0 (care@nearle.in)") + + resp, err := client.Do(req) + if err != nil { + fmt.Printf(" geocode request error: %v\n", err) + return "", "", false + } + defer resp.Body.Close() + + var rows []nominatimRow + if err := json.NewDecoder(resp.Body).Decode(&rows); err != nil || len(rows) == 0 { + return "", "", false + } + return rows[0].Lat, rows[0].Lon, true +} + +type row struct { + id int + address string +} + +func backfillTable(db *sql.DB, client *http.Client, table, idCol string) { + fmt.Printf("\n=== %s ===\n", table) + + query := fmt.Sprintf(` + SELECT %s, + TRIM(BOTH ', ' FROM CONCAT_WS(', ', address, suburb, city, state, postcode)) + FROM %s + WHERE (latitude IS NULL OR latitude = '' OR latitude = '0') + AND (address IS NOT NULL AND address <> '')`, idCol, table) + + rows, err := db.Query(query) + if err != nil { + log.Fatalf("%s: query error: %v", table, err) + } + var targets []row + for rows.Next() { + var r row + if err := rows.Scan(&r.id, &r.address); err != nil { + log.Fatalf("%s: scan error: %v", table, err) + } + targets = append(targets, r) + } + rows.Close() + + fmt.Printf("Found %d row(s) missing coordinates.\n", len(targets)) + + updated, skipped := 0, 0 + for i, r := range targets { + if i > 0 { + time.Sleep(rateLimit) + } + + lat, lon, ok := geocode(client, r.address) + if !ok { + fmt.Printf(" [%s=%d] %q — geocode FAILED, left untouched\n", idCol, r.id, r.address) + skipped++ + continue + } + + res, err := db.Exec( + fmt.Sprintf(`UPDATE %s SET latitude = $1, longitude = $2 WHERE %s = $3`, table, idCol), + lat, lon, r.id, + ) + if err != nil { + fmt.Printf(" [%s=%d] update error: %v\n", idCol, r.id, err) + skipped++ + continue + } + n, _ := res.RowsAffected() + if n > 0 { + fmt.Printf(" [%s=%d] %q -> lat=%s lon=%s\n", idCol, r.id, r.address, lat, lon) + updated++ + } + } + + fmt.Printf("%s: updated=%d skipped=%d\n", table, updated, skipped) +} + +func main() { + dsn := "host=66.116.207.225 port=5433 user=admin password=Package@123# dbname=nearledb sslmode=disable" + db, err := sql.Open("pgx", dsn) + if err != nil { + log.Fatalf("open error: %v", err) + } + defer db.Close() + + if err := db.Ping(); err != nil { + log.Fatalf("ping error: %v", err) + } + fmt.Println("Connected to nearledb.") + + client := &http.Client{Timeout: 10 * time.Second} + + backfillTable(db, client, "tenants", "tenantid") + backfillTable(db, client, "tenantlocations", "locationid") + + fmt.Println(strings.Repeat("-", 40)) + fmt.Println("Done.") +}